generic class, how to set the type in runtime?

10,830

Solution 1

You can create your class in another way, passing to the constructor the type that you want to use and exploit the dynamic keyword.

For example:

class MyGeneralClass
{
    dynamic myVariable;
    Type type;

    public MyGeneralClass(Type type)
    {  
        this.type = type;
        myVariable = Activator.CreateInstance(type);
        //And then if your type is of a class you can use its methods      
        //e.g. myVariable.MyMethod();
    }

    //If your function return something of type you can also use dynamic
    public dynamic Function()
    {
        return Activator.CreateInstance(type);
    }
}

Solution 2

Have a look at the MakeGenericType method. You can use it to create a Type instance (which can then be used to create an instance of that type) with generic argument values determined at runtime. Be aware, though, that you will need more reflection whenever dealing with the generic features of that object; you cannot create a variable in code whose type is a generic type with a variable type argument.

Solution 3

If you think about it, there is no reason to have a generic whose type is determined at runtime. Generics provide strong-typing, but this is only possible if you know the type at compile time. This is one of the strongest benefits of generics that it provides safety because you know what actions the object can perform. At runtime if you do not know what type you are getting, that defeats this purpose and you will have to delve into the powerful (but sometimes dangerous) world of reflection. For example,

List<DateTime> list = new List<DateTime>();
foreach (DateTime item in list)
   item.Minute + ": " + item.Hour

Because I know the type at COMPILE time, I can store it in a generic container and I have type safety when using it. I know the item has a Minute and Hour property.


If you want to perform an action based on what type the object is then you use the GetType() method. This is your window into runtime type identification and processing.

Result PerformQuery(object unknownObject)
{
    switch (object.GetType())
    {
         case typeof(ThisType): Do This; break;
         case typeof(ThatType): Do That; break;
    }
}

If you are going to do something with the object then you either know the Method name at compile-time which means you can use interfaces,

IFood hamburger = new Hamburger();
hamburger.BeEaten();

If you do not have or want to use an interface you can use reflection or dynamics, in this case I would use dynamic if I know what method I want to call.

Result PerformQuery(object unknownObject)
{
    if (object.GetType() == typeof(Pizza))  
    {
        dynamic pizza = unknownObject;
        pizza.BakeInOven() // since I checked and know it is type Pizza it is 'somewhat' safe to do this, unless Pizza class changes the name of BakeInOven method.
     }
 }

Lastly if you do not know the method you want to call at compile time you can use reflection.

  string methodName = CalculateWhatMethodShouldBe();
  MethodInfo myMethod = unknownObject.GetType().GetMethods().First(m => m.Name == methodName);    
  MethodInfo.Invoke(unknownObject);

Solution 4

I don't have enough information about your code, but maybe you could use simple polymorphism:

public interface IMyInterface
{
...
}

public class MyGenericClass<T> : IMyInterface
{
....
}

IMyInterface myClass = new MyGenericClass<typeNeeded>();

You could also mix this with factory design pattern, so that some other class will be responsible for instantiation of your types based on some runtime knowledge.

This of course assume that your MyGenericClass dosen't have methods like:

public T GetT(){...}

or if it does there is an additional relationship between all T'types so that all of them have common superclass lets say BaseT and you can change

public T GetT(){...}

to:

public BaseT GetT(){...}
Share:
10,830
Álvaro García
Author by

Álvaro García

Updated on June 04, 2022

Comments

  • Álvaro García
    Álvaro García almost 2 years

    I have created a generic class, but I know the type in runtime, not in design, so I would like to know how to set the type in runtime.

    For example, I have:

    public class MyGenericClass<T>
    {
    ....
    }
    

    Then I try to use it. I have a method in other class, that consume this generic class. In the constructor of this class, I receive as parameter the type that I want, so I have a type property in which I save the type that I need. So I am trying this:

    MyGenericClass<_typeNeeded> myClass = new MyGenericClass<typeNeeded>();
    

    But this does not work.

    How can I set the type in runtime in a class that I created?

    I am using C# 4.0.

    Thanks. Daimroc.

    EDIT: What I want to do is the following. I have a class that need to do some queries to the database. This queries always return the same information, a class, but the information that contains this class come from different tables. This is because I need to determinate what query to use. To decide what query to use I use the type that I receive.

    Is for this reason that I don't know in design the type, but is in runtime.

    I could use an interface that it would be implemented by to classes, and use the interface instantiated with the correct class, but this make me to have a switch or an if in the moment of the instantiation, and this is what I try to avoid, I want something more generic. Also, If I use this solution, to have an if in the moment of the instantion, I can create the generic class, so I would have only one class and it would be more maintainable.

  • O. R. Mapper
    O. R. Mapper almost 12 years
    typeNeeded is a variable in the OP's question, so new MyGenericClassy<typeNeeded>() will not compile.
  • 0lukasz0
    0lukasz0 almost 12 years
    Ok, good point. Anyway if we know that T is not any type but one of few types we know and we need simple and fast solution (without reflection), then we could use some factory with switch statement for type instantiation and stick to this solution.
  • O. R. Mapper
    O. R. Mapper almost 12 years
    True. Let's see whether the OP wants to go this path and needs more info.