Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create instance of generic class with dynamic generic type parameter

I need to create instance of a generic class like this:

 Type T = Type.GetType(className).GetMethod(functionName).ReturnType;
 var comparer = new MyComparer<T>(); // ERROR: "The type or namespace name 'T' could not be found"

I found this answer where this is possible only with reflection. But using reflection I get object which I need to cast to my generic type. I tried like this

 Type myGeneric = typeof(MyComparer<>);
 Type constructedClass = myGeneric.MakeGenericType();
 object created = Activator.CreateInstance(constructedClass);
 var comparer = (T)Convert.ChangeType(created, T);// ERROR: "The type or namespace name 'T' could not be found"

but get the same error. How to solve it?

Here is a complete example:

    public static bool Test(string className, string functionName, object[] parameters, object correctResult)
    {
        var method = Type.GetType(className).GetMethod(functionName);
        Type T = method.ReturnType;
        var myResult = method.Invoke(null, parameters);
        dynamic myResultAsT = Convert.ChangeType(myResult, T);
        dynamic correctResultAsT = Convert.ChangeType(correctResult, T);
        var comparer = new MyComparer<T>();    // Problem is here!!!       
        return comparer.Equals(myResultAsT, correctResultAsT);            
    }

The idea is to make a unit test which will call a function with parameters and compare its result with the correct result. But I need custom comparer, so I implement MyComparer which I cannot use because of a compiler error.

public class MyComparer<T> : IEqualityComparer<T>
{
    public bool Equals(T x, T y){/* some implementation*/}
}
like image 544
Dejan Avatar asked Mar 10 '23 03:03

Dejan


2 Answers

I found very simple solution to problem. There is no need to cast object to specific type T, just use dynamic keyword instead of casting

   Type myGeneric = typeof(MyComparer<>);
   Type constructedClass = myGeneric.MakeGenericType(T);
   object created = Activator.CreateInstance(constructedClass);
   dynamic comparer = created; // No need to cast created object to T

and then I can use comparer normally to call its methods like:

   return comparer.Equals(myResultAsT, correctResultAsT);

According to LueTm comments, it is probably possible to use reflection again and call comparer methods, but this solution looks much easier.

like image 108
Dejan Avatar answered Apr 20 '23 00:04

Dejan


Looks like you're almost there:

// t is a variable, so make it lowercase. This is where some of the confusion comes from
Type t = Type.GetType(className).GetMethod(functionName).ReturnType;
Type myGeneric = typeof(IEqualityComparer<>);

// You need to provide the generic type to make it generic with
// You want IEqualityComparer<T>, so:
Type constructedClass = myGeneric.MakeGenericType(t); 

// Now create the object
object created = Activator.CreateInstance(constructedClass);

// This is tricky without more context...
// You could try this, but to tell you more I would need to know where you use
// the comparer instance. Are you using it in a LINQ query, or in a Sort()?
// If so just cast it to a IEqualityComparer<YourType>, and
// make YourType whaterver you need it to be in the list or the query...
var comparer = (IEqualityComparer<object>)created;
like image 44
LueTm Avatar answered Apr 20 '23 00:04

LueTm