最近我遇到了一个C#泛型类型继承的有趣行为。想象一下以下示例:
class Foo<T> {}
class Bar<T> : Foo<T> {}
当我们使用封闭的泛型类型时,它们在继承方面的行为非常明显:
Console.WriteLine(typeof(Bar<int>).BaseType == typeof(Foo<int>)); // True
但是当我们使用开放泛型类型时,我们得到以下结果:
Console.WriteLine(typeof(Bar<>).BaseType == typeof(Foo<>)); // False
就个人而言,我希望这一行打印出True
。那么,我们来看看Bar<>
的基本类型:
Action<Type> discoverType = type =>
{
Console.WriteLine("Type name: " + type.ToString());
Console.WriteLine("Is generic type definition: " +
type.IsGenericTypeDefinition);
Console.WriteLine("Generic ags: " +
string.Join<Type>(", ", type.GetGenericArguments()));
Console.WriteLine("Types in generic:" +
string.Join<Type>(", ", type.GenericTypeArguments));
Console.WriteLine(Environment.NewLine);
};
Type baseType = typeof(Bar<>).BaseType;
discoverType(baseType);
Type openType = baseType.GetGenericTypeDefinition();
discoverType(openType);
上面的行产生以下输出:
Type name: GenericsReflection.Program+Foo`1[T]
Is generic type definition: False
Generic ags: T
Types in generic:T
Type name: GenericsReflection.Program+Foo`1[T]
Is generic type definition: True
Generic ags: T
Types in generic:
所以我们在Foo<T>
(第二个)中创建了一个类型(第一个),将T
替换为T
(异常,但有效,因为{{ 1}}也是运行时生成的类型)。如果我们有另一个继承自T
的类型,那么将生成新的基类型:
Foo<T>
所以,我的问题是:为什么我们需要class Another<T> : Foo<T> { }
Console.WriteLine(typeof(Another<>).BaseType == typeof(Bar<>).BaseType); // False
和Bar<>
之间以及Foo<>
和Another<>
之间的这些“代理”类型,为什么打开泛型类型可以'是从彼此继承?
答案 0 :(得分:3)
如果您为两个类型参数指定不同的名称(例如T1
和T2
,则更清楚。然后差异变得更加清晰:
Type name: Foo`1[T2]
Is generic type definition: False
Generic ags: T2
Types in generic:T2
Type name: Foo`1[T1]
Is generic type definition: True
Generic ags: T1
Types in generic:
它们实际上是不同的 - typeof(Bar<>).BaseType
具有由Bar<>
指定的类型参数) - 这只是你需要Bar<>
本身才能具体化才能得到具体的Foo<>
1}}。换句话说,typeof(Bar<>).BaseType
是构造的类型,而typeof(Foo<>)
则不是。class X1 : Foo<string> {}
class X2<T> : Foo<string> {}
class X3<TKey, TValue> : Foo<TKey> {}
class X4<TKey, TValue> : Foo<TValue> {}
。有关详细信息,请参阅Type.IsConstructedGenericType
(这是另一个为这两种类型提供不同值的属性)。
换句话说,请考虑以下三个声明:
typeof(Foo<>)
您是否希望每种类型的基本类型与Foo<string>
相同?在第一个和第二个案例中肯定是Foo<>
而不是{{1}} ...所以为什么你会期望它只是因为它使用类型参数而不是具体类型而不同?