【问题标题】:Pass Type dynamically to <T>将 Type 动态传递给 <T>
【发布时间】:2010-11-20 12:27:36
【问题描述】:

看到我有这样的情况......

object myRoledata  = List<Roles>() --> (some list or Ienumerable type)

现在我有一个通用方法,它从List&lt;T&gt; 创建一个 XML 对象 - 像这样的..

public string GetXML<T>(object listdata)  
{  
    List<T> objLists = (List<T>)Convert.ChangeType(listData, typeof(List<T>));  
    foreach(var obj in listdata)   
    {  
        //logic to create xml  
    }  
}

现在为了运行这个方法,我必须这样做:

string xml = GetXML<Roles>(myRoledata);

现在我不知道Type 可能会来找我传递给GetXML 方法。我有一个方法可以为不同的Types 调用GetXML,例如RolesUsers

现在我可以像这样在List&lt;&gt; 中获取Type

Type genericType = obj.GetType().GetGenericArguments()[0];

但不能这样通过

string xml = GetXML<genericType>(myRoledata);

无论如何我可以将任何genericTypes 传递给GetXML 方法吗?

【问题讨论】:

  • 您可能想在编译时(可能有多种)类型 T 是否未知的问题中说明清楚。如果它们是已知的,那么 Ulrik 的答案是有道理的,即停止强制转换并强制参数类型(可能有几个重载)。如果不知道,您必须以某种形式使用反射,而 Marks 的答案显示了实现这一目标的最简单且可能最好的方法。

标签: c# generics list


【解决方案1】:

为此,您需要使用反射;

typeof(SomeClass).GetMethod("GetXML").MakeGenericMethod(genericType)
         .Invoke(inst, new object[] {myRoleData});

其中instnull,如果它是静态方法,this 用于当前实例(在这种情况下,您也可以使用GetType() 而不是typeof(SomeClass)),否则为目标对象。

【讨论】:

  • 但这不是根本错误的吗?通过以这种方式调用方法,根本没有理由使用泛型,因为您抛弃了类型检查,并在运行时得到所有错误。
  • 我对这个问题的理解是他们想将它与泛型分离。如果这种解释不正确,那么您的方法显然更合适。这取决于场景;-p
  • 如果类是静态的怎么办?
  • @demodave 然后 inst 为空
【解决方案2】:

既然您在方法的第一行将您的 listdata 参数转换为 List,为什么不直接将方法签名更改为

public string GetXML<T>(List<T> listdata)

这样,您不必使用反射来获取泛型参数。

编辑:我看到您需要能够接受 IEnumerable 集合,而不仅仅是列表。因此,请考虑将您的方法签名更改为

public string GetXML<T>(IEnumerable<T> listdata)

【讨论】:

    【解决方案3】:

    你有正确的想法,但你使用了错误的方法。看看 Type.MakeGenericType 或 MethodInfo.MakeGenericMethod。这将比您的示例多几行,但应该很容易解决。

    GetGenericArguments() 可用于从列表中获取角色类型。这是不同的方式。

    顺便说一句:看起来您实现了某种 XML 序列化。确保在重新发明轮子之前检查现有的课程。 ;-)

    【讨论】:

      【解决方案4】:

      我不知道你的情况,但是是否可以将你的函数重写为:

      public string GetXML<T>(IEnumerable<T> listdata)  
      {  
          foreach(var obj in listdata)   
          {  
              //logic to create xml  
          }  
      }
      

      那么它可以被称为:

      List<Role> myList;
      GetXML(myList);
      

      您可以根据需要添加类型参数以支持它,直到您知道什么是实体类型。

      【讨论】:

        【解决方案5】:

        这是一个您可能希望避免解决的问题。 可以通过反射来动态调用方法而不用静态解析它们——但这有点违背了类型注释的全部意义。

        要么这样做:

        public string GetXML(IEnumerable listdata) {  
            foreach(object obj in listdata)   
                //logic to create xml  
        }
        

        ...您现在可以使用任何IEnumerable 调用它,或者将其写成“现代”方式:

        public string GetXML(IEnumerable<object> listdata) {  
            foreach(object obj in listdata)   
                //logic to create xml  
        }
        

        ...您可以通过GetXML(someEnumerable.Cast&lt;object&gt;()) 和在C# 4.0 中甚至直接通过协方差调用任何IEnumerable

        如果您需要元素运行时的类型,您可以在每个元素上使用.GetType() 来获取它,或者您可以将其作为参数传入(并为向后兼容提供覆盖):

        public string GetXML(Type elementType, IEnumerable<object> listdata) {  
            foreach(object obj in listdata)   
                //logic to create xml  
        }
        
        public string GetXML<T>(IEnumerable<T> listdata) {
            return GetXML(typeof(T),listdata.Cast<object>());
        }
        

        顺便说一下,如果您正在构建 XML,则字符串可能是一个不太可靠的返回类型选择:如果可能,您可以使用类似 XElement 的东西来代替 - 并获得 xml-validity 保证来启动。

        【讨论】:

        • 实际上,我建议不要直接使用IEnumerable&lt;object&gt;,至少在 C# 4.0 之前;由于类型参数缺乏变化,它可能不会做你想做的事。 IEnumerable 不仅可用。
        • 嗯,特别是对于 IEnumerable,由于 Linq .Cast&lt;&gt; 运算符,协方差不是问题。如果你想用IEnumerable&lt;object&gt; 参数调用一个方法,并且你有一个IEnumerable&lt;T&gt; myEnum 和一些T != object,你可以只做myEnum.Cast&lt;object&gt;(),而可读性和速度几乎没有损失。非泛型 IEnumerable 的风格缺点是缺乏明确的 IDisposable 支持,事实上 System.Collections 不在 VS.NET 添加到新文件的默认使用集中,而且它不太常见成语。
        • 无论如何,感谢您的评论 - 非通用 IEnumerable 也是一个不错的选择。
        猜你喜欢
        • 2010-11-10
        • 1970-01-01
        • 2020-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-06
        • 2016-12-11
        相关资源
        最近更新 更多