Friday, October 7, 2011

Generic Types and Static Members

One of my friend recently asked me a question that I think I should share with you. Say you have a Static Implementation of a Type T which you pass as Open Type in your Generic Class. Now the problem is how to get reference to the Static Members or invoke a method that is Static to the Type from the Open Type T.  In this post, I will demonstrate few implementations that help you getting the Static implementation.

First of all, According to C# documentation, any type allocates its static members once per Type rather than once per Open Type. Now lets define this using the code below :



public class MyType<T> where T : class
{
    public static int Counter;
    static MyType()
    {
        Counter = 0;
    }
    public MyType()
    {
        Counter++;
    }
}

Here this is actually a Generic Type that keeps track  of the count of objects that per type creates. The Type instance here MyType<T> is called Open Type and can take form of any Type.  Now lets create object of MyType.

MyType<string> ss = new MyType<string>();
ss = new MyType<string>();
ss = new MyType<string>();
MyType<object> oo = new MyType<object>();
oo = new MyType<object>();
oo = new MyType<object>();

Console.WriteLine(MyType<string>.Counter);

Console.ReadKey(true);

Now we create 3 instance of string and 3 of Object. Thus the Console.WriteLine should show 6, right? .... Wrong.. Actually we here create two types using Generic Type MyType, one is MyType<string> and another is MyType<object>. So here MyType generates its own closed type and each of the type creates its Type interface when first instance of the object is called for, or rather when the Static constructor is called.

Thus if I call MyType<X>.Counter it will show 0 and will create a new Type and call the Static constructor immediately.

Now this is very easy here to call static members of a Generic type, but what if we want to call Static member of the Type we pass into as Generic Argument. Lets see...

public class MyType
{
    public static void Try2CalMe()
    {
        Console.WriteLine("Wow. You finally called me!");
    }

    public void CallMe()
    {
        Console.WriteLine("Called CallMe");
    }
}

Lets say I have a class defined with one static method and one instance method. As I have already told you that Generic Static members create its memory when it creates it first object, the same is true for non generic types. Hence when an instance of MyType is passed within a Generic Type as Type argument, you would already have the memory allocated for the Type. Now let us consider using this Type for our Generic Class.
public class MyGenericClass<T> 
{
    T MyTObject { get; set; }
    public void InstanceMethod()
    {
        if (this.MyTObject != null)
            this.MyTObject.CallMe();
    }
}

Well the line which calls CallMe leads to a problem, because the Generic Type does not have any idea about what Type it is going to take. Say for instance if I pass int variable as T it will take that also. Well to fix this problem we can put a constraint to Type T with a Where clause. But that doesnt solve our problem either. Even though we can call CallMe we cannot call Try2CallMe. There are few approaches that you can take to solve this problem.


  1. Working with your friend Reflection :

    Reflection API can call any method from a Type. You can check whether the static Method is there in the type that is passed during Runtime and if exists you can call it. Lets try to call the method using Reflection :
    public class MyGenericClass<T> where T : MyType
    {
        public T MyTObject { get; set; }
        public void InstanceMethod()
        {
            if (this.MyTObject != null)
            {
                this.MyTObject.CallMe();
    
                //Calling static member using Reflection
    
                Type currentType = typeof(T);
                var method = currentType.GetMethod("Try2CalMe", BindingFlags.Static | BindingFlags.Public);
                if(method != null) // Method exists
                    method.Invoke(null, null); //We pass null as object for static member
    
            }
        }
    }

    So here we first pass MyType as constraint (even though it makes the type strict to inherited members of MyType) and calling the member using Reflection. Yes, it can call it absolutely.
  2. Putting it in intermediate Abstract base class:

    Another option, or probably better approach is to create an intermediate abstract base class for all your types and call it.  It is another option for you and probably a better one but remember, all of your members share the same static member here.
    public abstract class MyAType
    {
        public static void Try2CalMe()
        {
            Console.WriteLine("Wow. You finally called me!");
        }
        public virtual void CallMe()
        {
            Console.WriteLine("Called CallMe");
        }
    }
    public class MyGenericClass<T> where T : MyAType
    {
        public T MyTObject { get; set; }
        public void InstanceMethod()
        {
            if (this.MyTObject != null)
            {
                this.MyTObject.CallMe();
    
                //Calling static member Directly here
                MyAType.Try2CalMe();
                   
    
            }
        }
    }

    Here we create an abstract base class for MyType and we pass it as generic constraint. So any class that inherits from this base class can go as a Type for the class MyGenericClass and which in turn can call its static member directly.


There might be some other option available and known to you. I would like you to share that with me. 

I hope you like this post. 

Thanks. 

No comments:

Post a Comment

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.

Author's new book

Abhishek authored one of the best selling book of .NET. It covers ASP.NET, WPF, Windows 8, Threading, Memory Management, Internals, Visual Studio, HTML5, JQuery and many more...
Grab it now !!!