您提供的泛型接口直接从另一个泛型继承的示例掩盖了解决开放泛型之间兼容性的复杂性。有关这方面的更多信息,请阅读 Eric Lippert 最近的博客文章:为什么不自动推断约束?.
根据链接文章中 Eric 的注释,我不会尝试解决确定泛型接口是否在所有情况下都可以相互分配的一般情况。该解决方案的很大一部分将需要确定这两种类型的约束(如果有的话)是否完全相交。当一个开放的通用接口在某些情况下可分配给另一个但不能分配给另一个时,您还必须决定您希望假设方法返回什么,如果存在重叠但不重合的约束,就会发生这种情况。
更新
如果您将其打包在扩展方法中,那么按照您的建议比较直接继承“走上树”非常容易。但是,为了实际确定两个开放的泛型类型是否相等,您必须定义自己的比较,因为内置的相等比较不适用于由泛型类型检索GetInterfaces
或调用的类型定义:BaseType
typeof(Base<>) == typeof(Derived<>).BaseType; // Returns false
typeof(IBase<>) == typeof(Base<>).GetInterfaces()[0]; // Returns false
这可能源于开放的泛型类型检索自BaseType
或GetInterfaces()
具有空FullName
属性的事实,即使填充了Namespace
andName
属性。因此我也通过自己的GetFullName()
扩展方法定义了一个可选strongName
参数来确定是否包含完整的程序集名称。
因此,这里有一个相当紧凑的实现,用于比较开放泛型类型之间的直接继承或实现:
public static class TypeExtensions {
public static bool OpenIsAssignableFrom(this Type baseType, Type c, bool strongName = true) {
if (!baseType.IsGenericTypeDefinition || !c.IsGenericTypeDefinition) return false;
if (baseType.IsInterface)
return c.ImplementsOpenInterface(baseType);
Type testBaseType = c;
while (testBaseType != null) {
if (baseType.GetFullName(strongName) == testBaseType.GetFullName(strongName)) return true;
testBaseType = testBaseType.BaseType;
}
return false;
}
public static bool ImplementsOpenInterface(this Type sourceType, Type ifaceType, bool strongName = true) {
if (!ifaceType.IsInterface) return false;
return sourceType.GetInterfaces().Any(I => I.GetFullName(strongName) == ifaceType.GetFullName(strongName));
}
public static string GetFullName(this Type type, bool strongName = false) {
string name = type.FullName ?? "";
if (name.Length == 0)
name = type.Namespace + "." + type.Name;
if (strongName)
name += ", " + type.Assembly.FullName;
return name;
}
}
给定以下开放的通用接口:
namespace TypeExample {
public interface IBase<T> { }
public interface IDerived<T> : IBase<T> { }
public interface IDerived2<T> : IDerived<T> { }
public class Base<T> : IBase<T> { }
public class Derived<T> : Base<T>, IDerived<T> { }
public class Derived2<T> : Derived<T>, IDerived2<T> { }
}
以下所有内容都将返回true
:
typeof(IBase<>).OpenIsAssignableFrom(typeof(Base<>));
typeof(IBase<>).OpenIsAssignableFrom(typeof(Derived2<>));
typeof(Base<>).OpenIsAssignableFrom(typeof(Derived2<>));
typeof(IBase<>).OpenIsAssignableFrom(typeof(IDerived2<>));
这是使用构造的泛型类型和内置的 IsAssignableFrom 的直观相同结果:
typeof(IBase<string>).IsAssignableFrom(typeof(Base<string>));
typeof(IBase<string>).IsAssignableFrom(typeof(Derived2<string>));
typeof(Base<string>).IsAssignableFrom(typeof(Derived2<string>));
typeof(IBase<string>).IsAssignableFrom(typeof(IDerived2<string>));