5

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*/}
}
Community
  • 1
  • 1
Dejan
  • 966
  • 1
  • 8
  • 26
  • Your error messages don't make sense, are you sure that's what they say? – DavidG May 10 '17 at 17:55
  • @DavidG Full compiler error is: `The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)`. Why it doesn't make sense? – Dejan May 10 '17 at 17:58
  • 1
    Then you are not showing enough code, please show a [mcve] – DavidG May 10 '17 at 17:59
  • First, there's no such thing as a "template" in C#. There are only generics. Second, as you've already seen, there are adequate answers describing how to instantiate a generic type based on a type known only at run-time. Third, as most people find when they think they need this, they still run into problems trying to _use_ the dynamically instantiate object, because the code still needs to know at compile-time the type. – Peter Duniho May 10 '17 at 21:11
  • You seem to actually have several different problems; we need you to provide a good [mcve] that shows clearly what the _one_ problem at the moment is. Consider making this your highest-level problem, i.e. the question about the unit-testing, because it seems likely you are really having an [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) moment here. I'd be surprised if dynamically instantiating your object is needed at all. – Peter Duniho May 10 '17 at 21:11
  • @PeterDuniho Tnx for your comments. If you have `n` methods and each one you want to test for correctness, you have two options. The first one is to make another `n` methods where each one test correctness of specific method, so you does not need generics nor reflection. The second one is to make only one method which can test every method, but in this case you need reflection and generics. – Dejan May 11 '17 at 17:39
  • @PeterDuniho The `Test` method in my question is complete implementation of such generic unit test. Just call it with function name as string and provide function parameters as objects and it will test every function if its output is correct. You can correct me if there is another way without dynamically instantiating and reflection to make such test method. – Dejan May 11 '17 at 17:40

2 Answers2

7

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.

Community
  • 1
  • 1
Dejan
  • 966
  • 1
  • 8
  • 26
2

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;
LueTm
  • 2,366
  • 21
  • 31
  • Now, it is compiling, but it throws InvalidCastException in runtime. `Unable to cast object of type 'MyComparer1[System.Double]' to type 'MyComparer1[System.Object]'.` I use MyComparer insted of IEqualityComparer. – Dejan May 10 '17 at 18:26
  • I am using comparer instance only for `Equals` with two objects. I am not using it in a LINQ query. I write the usage of comparer in edited question. – Dejan May 10 '17 at 18:33
  • Just make the call to the Equals method with reflection as well, and it will work. Just write if you need assistance and I will edit the answer. – LueTm May 11 '17 at 07:53
  • There is no need for reflection and casting, I found very simple solution: `dynamic comparer = created;` then call Equals from comparer and it will compile and work. – Dejan May 11 '17 at 16:58