【问题标题】:Create instance of generic class with dynamic generic type parameter使用动态泛型类型参数创建泛型类的实例
【发布时间】:2017-05-10 17:48:18
【问题描述】:

我需要像这样创建一个通用类的实例:

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

我发现了这个answer,只有通过反射才能做到这一点。但是使用反射我得到了我需要转换为我的泛型类型的对象。我试过这样

 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"

但得到同样的错误。如何解决?

这是一个完整的例子:

    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);            
    }

这个想法是做一个单元测试,它将调用一个带参数的函数并将其结果与正确的结果进行比较。但我需要自定义比较器,所以我实现了 MyComparer,由于编译器错误,我无法使用它。

public class MyComparer<T> : IEqualityComparer<T>
{
    public bool Equals(T x, T y){/* some implementation*/}
}

【问题讨论】:

  • 你的错误信息没有意义,你确定他们是这么说的吗?
  • @DavidG 完整的编译器错误是:The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)。为什么没有意义?
  • 那你显示的代码不够,请显示minimal reproducible example
  • 首先,C# 中没有“模板”之类的东西。只有泛型。其次,正如您已经看到的,有足够的答案描述了如何基于仅在运行时才知道的类型来实例化泛型类型。第三,当大多数人认为他们需要这个时,他们仍然会在尝试使用动态实例化对象时遇到问题,因为代码仍然需要在编译时知道类型。
  • 您实际上似乎有几个不同的问题;我们需要您提供一个很好的minimal reproducible example,清楚地显示目前的one 问题是什么。考虑将其作为您的最高级别问题,即关于单元测试的问题,因为您似乎真的在这里遇到了XY Problem 时刻。如果根本需要动态实例化您的对象,我会感到惊讶。

标签: c# generics reflection casting


【解决方案1】:

我找到了非常简单的问题解决方案。无需将object 强制转换为特定类型T,只需使用dynamic 关键字而不是强制类型

   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

然后我可以正常使用比较器来调用它的方法,例如:

   return comparer.Equals(myResultAsT, correctResultAsT);

根据LueTmcmets,大概可以再次使用反射并调用比较器方法,但是这个解决方案看起来要容易得多。

【讨论】:

  • 这绝对是更好的解决方案。
  • @Dejan 这个 (T) 来自哪里? “MakeGenericType(T);”我在你的代码 sn-p 中没有看到它。
  • @metabuddy 请阅读问题中的第一个代码 sn-p (第一行)。在我的例子中,T 是某个方法的返回类型,但可以是任何类型。
【解决方案2】:

看起来你快到了:

// 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;

【讨论】:

  • 现在,它正在编译,但它在运行时抛出 InvalidCastException。 Unable to cast object of type 'MyComparer1[System.Double]' to type 'MyComparer1[System.Object]'.我使用 MyComparer insted of IEqualityComparer。
  • 我只对带有两个对象的Equals 使用比较器实例。我没有在 LINQ 查询中使用它。我在编辑的问题中写了比较器的用法。
  • 只需使用反射调用 Equals 方法,它就会起作用。如果您需要帮助,请写信,我会编辑答案。
  • 不需要反射和强制转换,我找到了非常简单的解决方案:dynamic comparer = created; 然后从比较器中调用 Equals 它将编译并工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多