Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic class from type parameter

Tags:

c#

types

generics

Is there a way to create a generic class from type parameter.

I have something like this:

public class SomeClass
{
    public Type TypeOfAnotherClass { get; set; }
}

public class OneMoreClass
{
}

public class GenericClass<T>
    where T : class
{
    public void DoNothing() {}
}

public class GenericClass
{
    public static GenericClass<T> GetInstance<T>(T ignored)
        where T : class
    {
        return new GenericClass<T>();
    }
}

What I want is to create a GenericClass from a Type. Ex:

var SC = new SomeClass();
SC.TypeOfAnotherClass = typeof(OneMoreClass);
var generic = GenericClass.GetInstance(SC.TypeOfAnotherClass);

Assert.AreEqual(typeof(GenericClass<OneMoreClass>), generic.GetType());

Here I expect to get instance of GenericClass<OneMoreClass> but I get GenericClass<Type>

I also tried using instance of that type. Ex:

var generic = GenericClass.GetInstance(Activator.CreateInstance(SC.TypeOfAnotherClass));

This time I get GenericClass<object>

Is there a way to accomplish this task?

like image 572
Merdan Gochmuradov Avatar asked Apr 25 '12 13:04

Merdan Gochmuradov


2 Answers

If you know the type you actually want (OneMoreClass) at build time, then you should just use it:

var generic = GenericClass.GetInstance<OneMoreClass>();

But I am assuming you don't know it at build time, and have to get the type at runtime. You can do it with reflection, but it isn't pretty, and is slow:

public class GenericClass
{
    public static object GetInstance(Type type)
    {
        var genericType = typeof(GenericClass<>).MakeGenericType(type);
        return Activator.CreateInstance(genericType);
    }
}

Since you don't know the resulting type at build time, you can't return anything but object (or dynamic) from the method.


Here is how much slower (for 100,000 creates)

public class GenericClass
{
    public static object GetInstance(Type type)
    {
        var genericType = typeof(GenericClass<>).MakeGenericType(type);
        return Activator.CreateInstance(genericType);
    }

    public static GenericClass<T> GetInstance<T>()
        where T : class
    {
        return new GenericClass<T>();
    }
}

    [Test]
    public void CanMakeGenericViaReflection_ButItsSlow()
    {
        var timer = new Stopwatch();
        var SC = new SomeClass();
        SC.TypeOfAnotherClass = typeof(OneMoreClass);

        timer.Start();
        for (int x = 0; x < 100000; x++)
        {
            GenericClass.GetInstance(SC.TypeOfAnotherClass);
        }
        timer.Stop();
        Console.WriteLine("With Reflection: " + timer.ElapsedMilliseconds + "ms.");

        timer.Restart();
        for (int x = 0; x < 100000; x++)
        {
            GenericClass.GetInstance<OneMoreClass>();
        }
        timer.Stop();
        Console.WriteLine("Without Reflection: " + timer.ElapsedMilliseconds + "ms.");
    }

Results:

With Reflection: 243ms.
Without Reflection: 2ms.

So a little over 100x slower.

The real thing to note about generics is that the <T>s in generics are resolved at build-time by the C# compiler, and the real class names are inserted. When you have to defer that until run-time, you end up paying the performance price.

like image 95
CodingWithSpike Avatar answered Oct 16 '22 13:10

CodingWithSpike


I'm not sure what exactly you are asking here. If I understood you properly, this is what you are looking for:

public class GenericClass
{
    public static GenericClass<T> GetInstance<T>(T ignored)
        where T : class
    {
        return new GenericClass<T>();
    }

    public static GenericClass<T> GetInstance<T>()
        where T : class
    {
        return new GenericClass<T>();
    }
}

Th usage:

  var generic1 = GenericClass.GetInstance<OneMoreClass>();
  var generic2 = GenericClass.GetInstance(new OneMoreClass());

Assertions:

Assert.AreEqual(typeof(GenericClass<OneMoreClass>), generic1.GetType());
Assert.AreEqual(typeof(GenericClass<OneMoreClass>), generic2.GetType());
like image 24
NaveenBhat Avatar answered Oct 16 '22 13:10

NaveenBhat