【问题标题】:GenericTypeDefinitionName from IEnumerable<object>来自 IEnumerable<object> 的 GenericTypeDefinitionName
【发布时间】:2016-07-04 10:24:47
【问题描述】:

注意这个问题是关于技术方面的,不是关于设计决策的。回答或评论不同的设计并回答这个问题,因为我只对这个特定问题的技术方面的性质感兴趣。

C# 6.0 我有这个方法,我传递了一个IEnumerable&lt;T&gt;

public void MyMethod(IEnumerable<object> list) { ... }

假设调用者在 MyClass[] 数组上调用它。
我想要的是泛型类型定义的类名,我有这个方法的实现:

public void MyMethod(IEnumerable<object> list)
{
    ...
    var name =
        from abstraction in list.GetType().GetInterfaces()
        where abstraction.IsGenericType
        && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>)
        from genericArgumentType in abstraction.GetGenericArguments()
        select genericArgumentType.Name;
    ...
}

现在在这种情况下(在方法的主体内),这会正确返回类的名称(例如,字符串 "MyClass")。所以我尝试将其重构为一个开放的通用扩展方法,如下所示:

public static string GetGenericTypeDefinitionName<T>(this IEnumerable<T> list)
{
    var name =
        from abstraction in list.GetType().GetInterfaces()
        where abstraction.IsGenericType
        && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>)
        from genericArgumentType in abstraction.GetGenericArguments()
        select genericArgumentType.Name;

    return name.Single();
}

然后像这样从MyMethod() 内部调用GetGenericTypeDefinitionName()

public void MyMethod(IEnumerable<object> list)
{
    ...
    var name = list.GetGenericTypeDefinitionName();
    ...
}

这也有效,但后来我意识到:'嘿!为什么不只是return typeof(T).Name'

结果它返回了字符串"Object",而我预期的结果与之前的实现相同(例如"MyClass")。

是否有可能获得预期的类型?在处理开放的泛型类型T 时,似乎所有信息都丢失了。 另外为什么我没有得到预期的类型? C# 的这种特定行为的技术细节是什么?

【问题讨论】:

  • 你能说明你是如何调用它的,以便typeof(T).Name返回"Object"吗?
  • 您是否有机会使用泛型方差? IEnumerable&lt;SomeType&gt; 可以分配给 IEnumerable&lt;object&gt;
  • @RenéVogt 我已经编辑了问题以显示扩展方法的调用方式和位置。
  • @QuantumHive 编辑中添加的示例与我在下面的答案中推测的完全一致;如果它实际上List&lt;SomeType&gt;,那么:您使用的是通用方差
  • @QuantumHive 要让它工作,你需要它是 MyMethod&lt;T&gt;(IEnumerable&lt;T&gt; list),等等

标签: c# .net generics reflection


【解决方案1】:

如果您使用泛型变量将某个引用类型 TIEnumerable&lt;T&gt; 分配给 IEnumerable&lt;object&gt;,您所描述的将会发生。

编译器将使用它所知道的在编译时选择&lt;T&gt;,在本例中为object。但是,如果您使用反射,您将获得原始名称 - 因为 实际对象 不会被方差技巧改变。

static void Main()
{
    IEnumerable<Foo> typed = new Foo[0];
    IEnumerable<object> untyped = typed;

    Console.WriteLine(typed.GetByGenerics());     // Foo
    Console.WriteLine(untyped.GetByGenerics());   // Object

    Console.WriteLine(typed.GetByReflection());   // Foo
    Console.WriteLine(untyped.GetByReflection()); // Foo
}
public static string GetByGenerics<T>(this IEnumerable<T> list)
{
    return typeof(T).Name;
}
public static string GetByReflection<T>(this IEnumerable<T> list)
{
    var name =
        from abstraction in list.GetType().GetInterfaces()
        where abstraction.IsGenericType
        && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>)
        from genericArgumentType in abstraction.GetGenericArguments()
        select genericArgumentType.Name;

    return name.Single();
}

这是因为你真的在打电话

Console.WriteLine(GetByGenerics<Foo>(typed));      // Foo
Console.WriteLine(GetByGenerics<Object>(untyped)); // Object

【讨论】:

  • 两者都会写在控制台实际类型上?
  • @Ehsan 我已将控制台输出添加到编辑中的示例
  • 我想我明白了。所以因为在我的问题MyMethod 在它的参数中定义了一个编译时类型(IEnumerable&lt;object&gt;),GetByGenerics 示例永远不会知道基于开放泛型类型T 的原始类型(IEnumerable&lt;Foo&gt;),正确?因为这是在编译时定义的,编译器无法推断?
猜你喜欢
  • 2021-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-05
相关资源
最近更新 更多