Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# - can convert from C# type to System.Type but not vice-versa

Tags:

c#

generics

I can create a generic class that takes, as its template parameter, a C# type, and then within the generic class use the System.Type information corresponding to that C# type:

public class Generic<T>
{
    public bool IsArray()
    {
        return typeof(T).IsArray();
    }
    public T Create()
    {
        return blah();
    }
}
Generic<int> gi = new Generic<int>();
Debug.WriteLine("int isarray=" + gi.IsArray());
Generic<DateTime> gdt;

But now let's say what I have, is a System.Type. I can't use this to instantiate my generic class:

FieldInfo field = foo();
Generic<field.FieldType> g;   // Not valid!

Is there some clever C# thing I can do, to convert a System.Type back to the original C# type? Or some other way, to create a generic that can (1) give me information about the System.Type, and (2) create objects of the associate C# type?

By the way, this is a very contrived example to explain the problem I'm trying to solve, don't worry too much about whether Generic makes sense or not!

like image 446
Betty Crokker Avatar asked Apr 30 '15 22:04

Betty Crokker


1 Answers

The only thing you can do is use reflection. This because while the int of Generic<int> is known at compile-time, the field.FieldType is known only at runtime.

Reflection example:

Type type = typeof(Generic<>).MakeGenericType(field.FieldType);

// Object of type Generic<field.FieldType>
object gen = Activator.CreateInstance(type);

But even here, from a Type (field.FieldType) you obtain another Type (type)

There are normally three ways of using this:

  • Full reflection: you use the object of type Generic<type> only through reflection. You create it through Activator.CreateInstance and from there you begin using Type.GetMethod() and Invoke()

Type type = typeof(Generic<>).MakeGenericType(field.FieldType);

// Object of type Generic<field.FieldType>
object gen = Activator.CreateInstance(type);
MethodInfo isArray = type.GetMethod("IsArray");
bool result = (bool)isArray.Invoke(gen, null);
  • Interfaces/base classes: you have a non-generic base class or interface that is common between all the Generic<T>. You use your object only though that interface/base class.

public class Generic<T> : IComparable where T : new()
{
    public bool IsArray()
    {
        return typeof(T).IsArray;
    }

    public T Create()
    {
        return new T();
    }

    public int CompareTo(object obj)
    {
        return 0;
    }
}

Type type = typeof(Generic<>).MakeGenericType(field.FieldType);
IComparable cmp = (IComparable)Activator.CreateInstance(type);
int res = cmp.CompareTo(cmp);
  • A generic method where you put all the handling of the Generic<T>. That is the only method that is used through reflection.

public static void WorkWithT<T>() where T : new()
{
    Generic<T> g = new Generic<T>();
    T obj = g.Create();
    Console.WriteLine(g.IsArray());
}

var method = typeof(Program).GetMethod("WorkWithT").MakeGenericMethod(field.FieldType);

// Single reflection use. Inside WorkWithT no reflection is used.
method.Invoke(null, null); 
like image 137
xanatos Avatar answered Oct 24 '22 11:10

xanatos