假设我有一个基本的继承结构:
public class Letter {...}
public class A : Letter {...}
public class B : Letter {...}
public class C : Letter {...}
public class Number {...}
public class One : Number {...}
我想定义一个Type
数组,只有从Type
继承的Letter
才有效。因此,示例数组可能如下所示:
(type)[] myArray = new (type)[] {typeof(A), typeof(B), typeof(C)};
但是作业
myArray[0] = typeof(One);
会失败。是否可以定义这样的结构?
答案 0 :(得分:3)
所有类型都是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>
,而不是取决于要求。
答案 1 :(得分:1)
我认为你不可能使用类型系统来强制执行此操作:typeof
返回System.Type
的实例。这是一个泛型类,并且类型系统没有任何方面可以强制只能存储某种类型的子类型。所以使用类型系统可能不是这样的。
但是有一些替代方案:
您可以使用contract contracts并希望您可以在编译时验证这一点。例如:
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
//...
}
答案 2 :(得分:0)
您始终可以定义Letter
的数组,并且只能将Letter
个对象和从其派生的类型分配给元素。但是,当您获得数组的元素时,编译器会认为它是Letter
。那可能就是你想要的。如果没有,您可以在运行时发现它的真正含义:
if (myArray[i] is A)
{
A objA = myArray[i] as A;
...
在这些向下广播中使用is
和as
运算符来验证向下转发是否有效。
或者,更好的是,您可以定义Letter
基类的行为属性和方法,以允许调用者获取派生类的行为,而无需知道对象的确切类是什么。
myArray[i].DrawSelf(Point ptStart)
并使各种派生类负责知道如何从给定位置开始绘制自己。