我目前正在使用C#进行泛型试验,并对自己提出了以下挑战:
给定泛型函数f<T>
,在编译期间验证T是来自给定集合[T1,T2,...,Tn]的类型。例如,如果在f<T>
我们有
CompileTimeAssert<T>.isContainedIn<TypeList<string, int, bool>>();
然后f<int>
应该编译,而f<double>
不应该编译。
我还没到那儿。这就是我到目前为止所做的:
interface ContainsType<T> {}
class TypeList<T1>: ContainsType<T1> {}
class TypeList<T1, T2>: TypeList<T2>, ContainsType<T1> {}
class TypeList<T1, T2, T3>: TypeList<T2, T3>, ContainsType<T1> {}
class TypeList<T1, T2, T3, T4>: TypeList<T2, T3, T4>, ContainsType<T1> {}
// add longer type lists to taste
class CompileTimeAssert<T>
{
public static void isContainedIn<TypeList>()
where TypeList: ContainsType<T> {}
public static void isContainedIn<TypeList>(TypeList tl)
where TypeList: ContainsType<T> {}
}
鉴于上面的代码,以下编译(如预期的那样):
// uses first overload
CompileTimeAssert<int>.isContainedIn<TypeList<string, int, bool>>();
var myTypeList = new TypeList<string, bool>();
CompileTimeAssert<string>.isContainedIn(myTypeList); // uses second overload
以下内容未按预期编译:
CompileTimeAssert<short>.isContainedIn<TypeList<string, int, bool>>();
var myTypeList = new TypeList<string, bool>();
CompileTimeAssert<double>.isContainedIn(myTypeList);
这一切都非常可爱,但也没用。如果可以做到以下几点会更有用:
void f<T>()
{
CompileTimeAssert<T>.isContainedIn<TypeList<string, int, bool>>();
}
然后编译f<int>
,f<double>
导致编译错误。
唉,上面给出的f<T>
无法编译(无论具有具体类型的任何调用)。
我收到以下错误(在Mac OS X上使用MonoDevelop):
错误CS0311:类型
TypeList<string,int,bool>' cannot be used as type parameter 'TypeList' in the generic type or method 'CompileTimeAssert<T>.isContainedIn<TypeList>()'. There is no implicit reference conversion from
TypeList'到 'ContainsType'
我有点理解为什么这不起作用,但到目前为止,我还没有找到一个可行的替代方案。有没有人对C#中是否有我想要的东西有任何想法?
感谢。
答案 0 :(得分:3)
您的挑战的目标是无用的,因为.NET的泛型与编译时构造一样是运行时构造。即使您的程序编译没有错误,仍然可以通过反射扩展您的泛型,传递“未批准”类型。
我理解你的来源(我非常喜欢Andrei Alexandrescu的那本书)但是要理解C#泛型的重要一点是泛型不是C ++模板。除了小的句法相似之外,它们甚至不是那么接近:“不是同一个球场,不是同一个联赛,甚至不是同一项运动”。需要进行编译时验证排序“整数是正常的,双打不正确”的主要驱动力是你可以隐式访问操作:a+b
如果模板扩展时提供的a
和b
类型允许+
操作。它在C#中不一样:如果您希望对传递给泛型的值执行操作,你必须通过类型约束明确地规定该操作的存在,或者提供一种执行该操作的明确方式(委托,接口等)。无论哪种方式,告诉编译器你的模板适用于int nut而不是for dubles什么都不买你。
答案 1 :(得分:0)
我不知道你是如何通过泛型来做的。
这样一个通用类的目的是什么(没有编译,它就是这里的例子):
public class GenericClass<T>
where T : A
where T : B
where T : C
{
public T MyMember { get; set; }
public GenericClass(T myMember)
{
this.MyMember = myMember;
}
}
MyMember
实现什么逻辑,因为3个类没有任何共同点?where
约束使用此接口或基类。 答案 2 :(得分:0)
这不起作用,因为C#泛型函数只需要根据约束传递类型检查。不考虑实际类型参数,因为泛型不是专门的。
你唯一承诺的是(隐含地)T : object
。
所以编译器类型检查
CompileTimeAssert<object>.isContainedIn<TypeList<string, int, bool>>();
并且这(正确)无法编译。
另一方面,你可以写:
void f<T>() where T : Form
{
CompileTimeAssert<T>.isContainedIn<TypeList<string, Control>>();
}
它会编译,因为每个Form
都是一个Control
。