【问题标题】:Setting generic type at runtime在运行时设置泛型类型
【发布时间】:2015-08-17 16:12:48
【问题描述】:

我有课

public class A<T>
{
   public static string B(T obj)
   {
       return TransformThisObjectToAString(obj);
   }
}

上面对字符串的使用纯粹是示例性的。我可以在已知/指定类型上调用这样的静态函数:

string s= A<KnownType>.B(objectOfKnownType);

如果我事先不知道 T,我该如何进行此调用,而我有一个 Type 类型的变量来保存该类型。如果我这样做:

Type t= typeof(string);
string s= A<t>.B(someStringObject);

我得到这个编译器错误:

Cannot implicitly convert type 't' to 'object'

【问题讨论】:

标签: c# generics


【解决方案1】:

您不能直接执行此操作,但可以使用反射在运行时提供类的类型参数。我还没有测试过这个,但是这样的东西应该可以工作:

// We want to do something like this:
//    object o = "Hello"
//    Type t = o.GetType(); 
//
// This is pseudo-code only:
//    string s = A<t>.B(o); 

string InvokeA(object o) {
  // Specify the type parameter of the A<> type
  Type genericType = typeof(A<>).MakeGenericType(new Type[] { o.GetType() });
  // Get the 'B' method and invoke it:
  object res = genericType.GetMethod("B").Invoke(new object[] { o });
  // Convert the result to string & return it
  return (string)res;
}

当然,问题是这是否真的是你需要的——如果你对作为参数给出的对象一无所知,你也可以只使用 object.不过,我可以想象在某些情况下这会很有用,所以我想你可以尝试使用它。

【讨论】:

  • 哦,我没有意识到你可以将A&lt;&gt; 传递给typeof()。你的代码绝对比我的干净。干得好。
  • 我不太喜欢你可以在 C# 中编写A&lt;&gt;,因为它不是真正的类型。这是一件很奇怪的事情。无论如何,我想它有时很有用:-)。
  • A&lt;&gt;,和Outer&lt;,&gt;.Inner&lt;,,,&gt;之类的东西对于typeof“参数”很有用,但这是一件好事,在其他情况下是不允许的,因为你是对的C# 类型。
  • A&lt;&gt; 是一个不完整的类型。我在工厂的上下文中使用了这样的构造,在那里我正在寻找从使用 CRTP 的通用基础继承的类型。我将从程序集中加载所有类型,看看是否可以基于该类型实例化泛型,然后查看该类型是否继承自实例化。如果是这样,它就是我的工厂可以创建的类型之一。
【解决方案2】:

在框架和 CLR 中绝对支持这一点 - 只是在 C# 中并不优雅。不过,借助辅助方法,您可以完成我认为您想要的:

public class A<T>
{
    public static string B(T obj)
    {
        return obj.ToString();
    }
}

public class MyClass
{
    public static void DoExample()
    {
        Console.WriteLine(ExecuteB("Hi"));
        Console.WriteLine(ExecuteB(DateTime.Now));
    }

    public static object ExecuteB(object arg)
    {
        Type arg_type = arg.GetType();
        Type class_type = typeof(MyClass);
        MethodInfo mi = class_type.GetMethod("ExecuteBGeneric", BindingFlags.Static | BindingFlags.Public);
        MethodInfo mi2 = mi.MakeGenericMethod(new Type[] { arg_type });
        return mi2.Invoke(null, new object[] { arg });
    }

    public static object ExecuteBGeneric<T>(T arg)
    {
        return A<T>.B(arg);
    }

【讨论】:

    【解决方案3】:

    你不能。泛型类型标识符必须在编译时知道。

    编辑

    与其他帖子一样,通过动态生成方法并调用它似乎是可能的——这当然有危险。有关更多信息,请参阅 Thomas 和 Dathan 的帖子。

    【讨论】:

    • 我想我误读了这个问题。除了您所说的之外,真的无法添加任何其他内容。
    • 更准确的说法是没有静态类型的方法可以做到这一点;或者也许没有惯用的方式。但正如 Tomas 和我的回应所示,该方法可以动态解析并在运行时使用任意参数调用。
    【解决方案4】:

    你不能。但是您针对所提供的案例提出了错误的问题。在这种情况下(与 99% 的情况一样),您真正需要的只是一个类型约束

    试试:

    public class A<T> where T : object
    

    或者,如果 T 是已知类、子类或接口,那么最好使用

    public class A<T> where T : YourAbstractClass
    

    还存在其他类型约束。更多详情:http://msdn.microsoft.com/en-us/library/d5x73970(VS.80).aspx

    作为一般说明,在学习一门新语言时,您通常必须广泛考虑您想要实现的目标,而不是具体找到您想要的目标。这很像现实世界的口头语言。这是通过阅读字典学习德语并强制将单词变成英语语法,或者学习语法并拿起单词之间的区别。是的,说德语的人会听懂字典里的人说话,但每句的 WTF 会高得多。

    【讨论】:

      【解决方案5】:

      我根据这里的一些答案以及网络上的其他地方创建了这个帮助方法。

      用法:

      InvokeGenericMethodWithRuntimeGenericArguments( MyMethodWithGenericType<IType>, new[] {MyRuntimeGenericType}, null);
      

      方法:

      public static object InvokeGenericMethodWithRuntimeGenericArguments(Action methodDelegate, Type[] runtimeGenericArguments, params object[] parameters)
              {
                  if (parameters == null)
                  {
                      parameters = new object[0];
                  }
                  if (runtimeGenericArguments == null)
                  {
                      runtimeGenericArguments = new Type[0];
                  }
      
                  var myMethod = methodDelegate.Target.GetType()
                               .GetMethods()
                               .Where(m => m.Name == methodDelegate.Method.Name)
                               .Select(m => new
                               {
                                   Method = m,
                                   Params = m.GetParameters(),
                                   Args = m.GetGenericArguments()
                               })
                               .Where(x => x.Params.Length == parameters.Length
                                           && x.Args.Length == runtimeGenericArguments.Length
                               )
                               .Select(x => x.Method)
                               .First().MakeGenericMethod(runtimeGenericArguments);
                  return myMethod.Invoke(methodDelegate.Target, parameters);
              }
      

      【讨论】:

        【解决方案6】:

        试图在运行时替换类型参数将破坏类型 saftey 的全部目的,这是由 C# 编译器强制执行的。C# 编译器确保在编译时指定类型参数并且在运行时类型参数没有歧义。I怀疑你可以在运行时用泛型类型替换类型参数。指定类型“Type”的类型参数几乎就像拥有一个未绑定的泛型类型。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-06-30
          • 2020-08-19
          • 1970-01-01
          相关资源
          最近更新 更多