GenericTypeDefinitionName from IEnumerable<object>

本文关键字:object gt from lt GenericTypeDefinitionName IEnumerable | 更新日期: 2023-09-27 17:54:24

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

C# 6.0中,我有这个方法,我传递IEnumerable<T>:

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#的这种特殊行为的技术细节是什么?

GenericTypeDefinitionName from IEnumerable<object>

如果您使用通用方差将某种参考类型TIEnumerable<T>分配给IEnumerable<object>,您所描述的情况将会发生。

编译器将使用它所知道的在编译时选择<T>,在本例中是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