这个问题难以理解,我希望以下代码片段能说清楚:
public class DemoClass<TBase> where TBase : class
{
public void DemoMethod<T>(T target) where T : TBase
{
//The following line causes a design-time error: Type argument 'T' does not satisfy the 'Class' constraint for type parameter 'T'.
WeakReference<T> demoRef = new WeakReference<T>(target);
}
}
WeakReference
需要一个满足T
约束的类型class
。到目前为止,这么好,但是......
为什么编译器无法检测T
实际上是什么,因为(实际上)T : TBase : class
?
答案 0 :(得分:5)
为什么编译器不能检测到T实际上是这样,因为(实际上)T:TBase:class?
因为那根本不是真的。除Poke在answer中指出的内容之外,由于所有值类型都继承自 object
,这也是非法的:
var dc = new DemoClass<object>();
dc.DemoMethod(1); //woops, just attempted to create a WeakReference<int>
当涉及到价值类型时,您的推理就会崩溃。做作?是的,但完全合法,因此编译器没有选择权并且必须将您的代码视为非法。
更新
在下面的Jon Hana的评论中说明T
上面的代码并非真正int
,其object
和1
被隐式包装,这绝对不是真的。请考虑DemoMethod
的以下变体:
public T DemoMethod<T>(T target) where T : TBase
{
return target;
}
以下代码:
var dc = new DemoClass<object>();
var i = dc.DemoMethod(1);
i
为int
,而不是object
。此外,以下将正确执行:
long i = dc.DemoMethod(1);
这也证明T
不能是盒装int
,因为隐式转换会在运行时失败;除了类型本身之外,您无法将值类型取消装箱。
当然,你总是可以明确地设置T
,这也可以很好地编译:
dc.DemoMethod<int>(1);
答案 1 :(得分:3)
让我们检查the documentation INT_MAX
实际含义:
<强>
T : class
强>type参数必须是引用类型;这也适用于任何类,接口,委托或数组类型。
不幸的是,如果where T : class
是例如接口,则已经满足。因此,您可以构建一个简单的示例,您可以看到传递地应用T
将不起作用:
T : class
如果您现在创建public interface ITest { }
public struct Test : ITest { }
,则您满足类型约束,因为此处DemoClass<ITest>
是“类”。但是当您调用方法ITest
时,虽然DemoMethod<Test>
确实继承了T
,但您没有Test
的引用类型。
通常,那些特殊的泛型类型约束不遵循继承规则。这就是为什么它们是单独定义的,并且尚未由类型系统建立。它们作为一种特殊的语法存在,因为类型系统无法表达其他的构造。