Click here to Skip to main content
15,883,978 members
Articles / Programming Languages / C#

Using Generic Extension Methods

Rate me:
Please Sign up or sign in to vote.
3.00/5 (18 votes)
24 Oct 2008CPOL5 min read 106.1K   19   5
Explains the concepts of Generics (introduced in C# 2.0) and Extension Methods (introduced in C# 3.0) and how to mix those together to get a new concept of Generic Extension Methods that will make a difference in our class designs.

Introduction

In this article, I will explain the concepts of Generics (introduced in C# 2.0) and Extension Methods (introduced in C# 3.0), and how to mix those together to get a new concept of Generic Extension Methods that will make a difference in our class designs.

Background

This article talks about two concepts merged together: Generics and Extension Methods, so in order to understand it, you got to be familiar with both concepts, so I am going to summarize each of them.

Generics is a new feature in C# 2.0 and the Common Language Runtime, which introduces the concept of type parameters, so you can just specify your type for a class or a method at runtime.

Extension Methods is a new feature in C# 3.0 and the Common Language Runtime, which allows existing classes to use extra methods (extension methods) without being implemented in the same class or obtained by inheritance.

Generics Sample

Below is a sample of using Generics with methods; the same concept apply to classes, but for the purposes of this article, we are going to use a generic method:

C#
public class SomeClass
{
    ///Using of one type as parameter
    public string Method1<TInput>(TInput obj1)
    {
        //return a serialized object as string for example
    }

    ///Using of two types as parameters
    public TReturn Method2<TReturn, TInput>(TInput obj1)
    {
        //map data from an instance of TInput type
        //to TReturn type and return an
        //instance of TReturn type
    }

    ///Using of three types as parameters
    public TReturn Method3<TReturn, TInput1, TInput2>(TInput1 obj1, TInput2 obj2)
    {
        //Map data from an instance of TInput1 and TInput2 types to an
        //instance of TReturn type and return that instance
    }
}

Fairly simple. For a certain method, you declare types before the () and use them as your input or output or both, or even inside the method body without being used as input or output.

Extension Methods Sample

Below is a sample of using Extension Methods:

C#
public class MyClass{

    private string m_Name;
        public string Name{
        get{return m_Name;}
        set{m_Name=value;}
    } 
}
 
public ExtensionMethodsClass{
    public static string DoSerialize(this MyClass entity){
    //Serialze the entity object and return its string represenatation
    }
}

Note the use of the this keyword in the DoSerialize method. This indicates that an instance of MyClass will only be able to call this method like this:

C#
MyClass instance=new MyClass();
instance.DoSerialize(); 

Although the DoSerialize method is not a member of MyClass, you can just call it like it is a member!

The input parameter of the DoSerialize method, the this keyword, tells us that an instance of type MyClass will call DoSerialize as it is a member in it.

Pretty ... ha ! Now, let's take a look at how to merge the two concepts together, it's fabulous actually ;)

Generic Extension Method

As we have seen earlier, the this keyword will determine which type will call the extension method as it's a member. Let's replace the type of the this keyword (MyClass) with the Generic Type T, as follows:

C#
public ExtensionMethodsClass{
    public static string DoSerialize<T>(this T entity){
    //Serialze the entity object and return its string represenatation
    }
}

Up to here, any type will use this extension method to make a serialization using the DoSerialize method, which is good and bad at the same time! Good because we are generic in our types and a lot of types will use this method and there is no need to implement the same extension method over and over again; bad because it will receive any type! What if we pass a non-serializable type!?? Oops ... I think this will make a problem for us that we should take care of!

Using the 'Where' Clause with Generics

Here comes the role of the where clause. The where clause is used to determine the type used as generic, more likely the specifications of the type we are going to use as generic. We can determine if it should have a default constructor too, or the interfaces it should be implementing, and focus on that, because it is the key to a good implementation as we would see at the end of this article. No further explanation, let's take a look at the code sample below:

C#
public ExtensionMethodsClass{
    public static string DoSerialize<T>(this T entity) where T: MyClass{
    //Serialze the entity object and return its string represenatation
    }
}

Using the above code, we determine that the generic type used should be of type MyClass or inherited from MyClass; seems good, but actually, it is not so far! Because, by determining the type to be MyClass, we are not much different from our first implementation using:

C#
public static string DoSerialize(this MyClass entity){ ...}

which we wanted to expand from the whole beginning! So, how can we get it all together ?!

When and How to Use it ? And the Effect on Design

Usually, we have an application, we have a lot of business classes in it, and most probably, we have that base class or interface for all these business classes and common behaviours are replaced within that class or interface. Now, these behaviours should be only implemented for our business entities, and not any other .NET classes, for example. We have here two options:

  1. Place these behaviours in the base class or interface and all entities will inherit it (what is usually done).
  2. Make a generic extension method where the type is the base class or interface (a new way of doing it).

Both solutions will do the trick for you, but it will affect your design on the long run; you see, inheritance always ties our code with its limitations. It is not that this is a bad thing; it is just the way inheritance works; and sometimes, solving problems using inheritance will lead to major problems in the design and then to maintenance problems in certain cases. So, you have to be careful doing so. On the other hand, Generic Extension Methods will make us avoid those limitations: by inheritance, because it is expandable. Let's take a look at the following code which will illustrate a good example of Generic Extension Methods and help in the design as well:

C#
public class BaseClass{
}

public class MyClass: BaseClass, ISerializable{
}

public class YourClass: BaseClass, ISerializable{
}

public class HisClass: ISerializable{
}

public class HerClass: ISerializable{
}

public ExtensionMethodsClass{
    public static string DoSerialize<T>(this T entity) where T: BaseClass{
    //Serialze the entity object and return its string represenatation
    }
}

As we can see in the above code, we have four ordinary classes and an Extension Methods class; the four classes are serializable, the classes MyClass and YourClass will have the ability to use the Extension Method DoSerialize, because they inherit from the BaseClass class; on the contrary, the classes HisClass and HerClass will not have that option.

But in this context, a member here at CodeProject noted how using:

C#
public static string DoSerialize<T>(this T entity) where T: BaseClass { ...}

will be different from using :

C#
public static string DoSerialize(this BaseClass entity){ ...}

Actually, in this context, it won't be much of a difference... but, let's take a look at the flexibility we could have by using the following example:

C#
//Our Interfaces
public interface IFirstInterface
{
}

public interface ISecondInterface
{
}

//Our Classes
public class AClass : IFirstInterface
{
}
public class BClass : ISecondInterface
{
}
public class CClass: IFirstInterface , ISecondInterface
{
}

//Our Extension Methods Class
public static class ExtensionMethodsClass
{
    public static void DoSerialize<T>(this T entity) 
    where T: IFirstInterface, ISecondInterface
    {
    }
}

I think the code is obvious enough to explain itself :). Simply an instance of CClass will only be able to call the Extension Method DoSerialize(), and neither AClass nor BClass:

C#
AClass A=new AClass();
BClass B=new BClass();
CClass C=new CClass();
A.DoSerialize();//Compiler Error
B.DoSerialize();//Compiler Error
C.DoSerialize();//Works Fine

Conclusion

Generics and Extension Methods are two great concepts which really help in our design of classes; they help us in minimizing our code and use the concept of Code Refactoring. In addition, maintenance will be always much easier using Extension Methods. But, I think we should always keep an eye on the fact that our design demands will control what technologies we should use, not the other way around. Understanding the technologies we use will help us more in taking the major decisions in our designs!

History

  • Created the article: September 4th, 2008
  • Updated the article: September 5th, 2008

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Jordan Jordan
Samer is a Computer and Digital Electronics Engineer who lives in Abu Dhabi UAE, worked on a fair number of personal projects as part of fun or freelancing, mostly related to integration between hardware and software (e.g Security Systems, Sensors, Cameras, Bluetooth ... etc), which gave him a powerful knowledge in this area and the ability to invent various complex designs to adhere for external elements effect those systems.

Through his career path he worked on a fair number of enterprise projects with well known software companies, which showed him the various aspects of an enterprise applications.

You may contact Samer through his e-mail: SamerX@outlook.com or Twitter: SamerX as well.

Comments and Discussions

 
GeneralMy vote of 5 Pin
FtpDaemon18-Nov-12 12:55
FtpDaemon18-Nov-12 12:55 
GeneralRate this Article Pin
Samer Aburabie12-Sep-08 9:13
Samer Aburabie12-Sep-08 9:13 
QuestionHuh? Pin
PIEBALDconsult4-Sep-08 16:49
mvePIEBALDconsult4-Sep-08 16:49 
AnswerRe: Huh? Pin
Samer Aburabie5-Sep-08 0:24
Samer Aburabie5-Sep-08 0:24 
Actually its an excellent question ... you see in the context used its the same ... but let me give you this sample which will help to clearify how it would be good :

//Our Interfaces
public interface IFirstInterface
    {
    }
public interface ISecondInterface
    {
    }


//Our Classes
public class AClass : IFirstInterface
    {
    }
public class BClass : ISecondInterface
    {        
    }
public class CClass: IFirstInterface , ISecondInterface
    {
    }


//Our Extension Methods Class
public static class ExtensionMethodsClass
    {
public static void DoSerialize<T>(this T entity) where T: IFirstInterface, ISecondInterface
        {
        }
    }


Can you see the trick here !? only an instance of class C will be able to call the extension method ... instances of class A and B won't ... because they dont implement both of the interfaces ... as below:

AClass A=new AClass();
BClass B=new BClass();
CClass C=new CClass();

A.DoSerialize();//Compiler Error
B.DoSerialize();//Compiler Error
C.DoSerialize();//Works Fine


All I meant in the article is to look to the big picture of it ... specially the design problems we could face ...

Thank you for the question ... and I will update the article to clearify this in it ... really thank ya Smile | :)

Sincerely Samer Abu Rabie

Note: Please remember to rate this post to help others whom reading it.


GeneralRe: Huh? Pin
PIEBALDconsult5-Sep-08 4:27
mvePIEBALDconsult5-Sep-08 4:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.