将类型定义为类型的超级集
本文关键字:类型 定义 | 更新日期: 2023-09-27 18:29:49
假设我有一个基本的继承结构:
public class Letter {...}
public class A : Letter {...}
public class B : Letter {...}
public class C : Letter {...}
public class Number {...}
public class One : Number {...}
我想定义一个Type
的数组,这样只有从Letter
继承的Type
才有效。因此,一个样本数组可能看起来像:
(type)[] myArray = new (type)[] {typeof(A), typeof(B), typeof(C)};
但是任务
myArray[0] = typeof(One);
将失败。有可能定义这样一个结构吗?
所有类型都是System.Type
的实例,静态相同。因此,使用传统阵列进行此操作是不可能的。一种选择是创建一个只允许通过类型参数添加的数据结构:
internal sealed class LetterTypeSet : IReadOnlyCollection<Type>
{
readonly ISet<Type> types = new HashSet<Type>();
public IEnumerator<Type> GetEnumerator() => this.types.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
public int Count => this.types.Count;
public bool Add<T>()
where T : Letter
{
return this.types.Add(typeof (T));
}
}
这将允许:
var letters = new LetterTypeSet();
letters.Add<A>(); //ok
letters.Add<B>(); //ok
letters.Add<One>(); //CS0311 "The type 'One' cannot be used ..."
注意:我使用ISet<T>
作为底层数据结构,根据需要,您可能希望也可能不希望使用List<T>
。
我认为您不可能使用类型系统来强制执行这一点:typeof
返回System.Type
的实例。这是一个泛型类,类型系统的任何方面都不能强制要求只能存储特定类型的子类型。因此,使用类型系统可能不是解决问题的方法。
然而,也有一些替代方案:
-
您可以使用合同,并希望您可以在编译时验证这一点。例如:
Type[] myArray = new (type)[] {typeof(A), typeof(B), typeof(C)}; //... Contract.Ensures(Contract. ForAll (myArray,t => typeof(Letter).IsAassignableFrom(t));
然而,请注意,这是一个无法确定的问题,合同验证器只能给出一个保守的答案。
-
你可以定义一个数据结构(可能是
ICollection<Type>
的一个子集,在前门强制执行public class SubTypeList<T> : IList<Type> { private readonly List<Type> innerList = new List<Type>(); public void Add (Type type) { if(typeof(T).IsAssignableFrom(type)) { innerList.Add(type); } else { throw new ArgumentException("The given System.Type must be an inherit from T!"); } } //Implement other methods //... }
您始终可以定义Letter
的数组,并且只有Letter
对象和从中派生的类型可以分配给元素。但是,当您获得数组的一个元素时,编译器会认为它是Letter
。这可能就是你想要的。如果没有,您可以在运行时发现它的真实情况:
if (myArray[i] is A)
{
A objA = myArray[i] as A;
...
在这些下变频中使用is
和as
运算符来验证下变频是否有效。
或者,更好的是,您可以定义Letter
基类的行为属性和方法,使调用者能够获得派生类的行为,而不必知道对象的确切类是什么。
myArray[i].DrawSelf(Point ptStart)
并使各种派生类负责知道如何从给定位置开始绘制自己。