为什么当“x as Y”工作正常时,此强制转换无效
本文关键字:常时 无效 转换 工作 as 为什么 | 更新日期: 2023-09-27 18:35:12
我昨天偶然发现了这个奇怪的情况,其中t as D
返回一个非空值,但(D)t
导致编译器错误。
由于我很着急,我只是用了t as D
并继续前进,但我很好奇为什么演员表无效,因为t
真的是D
。谁能阐明为什么编译器不喜欢演员阵容?
class Program
{
public class B<T> where T : B<T> { }
public class D : B<D> { public void M() { Console.Out.WriteLine("D.M called."); } }
static void Main() { M(new D()); }
public static void M<T>(T t) where T : B<T>
{
// Works as expected: prints "D.M called."
var d = t as D;
if (d != null)
d.M();
// Compiler error: "Cannot cast expression of type 'T' to type 'D'."
// even though t really is a D!
if (t is D)
((D)t).M();
}
}
编辑:玩,我认为这是一个更清晰的例子。在这两种情况下,t
都被限制为B
,并且可能是D
。但是泛型的情况不会编译。C# 在确定强制转换是否合法时是否只是忽略泛型约束?即使它确实忽略了它,t
仍然可能是D
;那么,为什么这是编译时错误而不是运行时异常呢?
class Program2
{
public class B { }
public class D : B { public void M() { } }
static void Main()
{
M(new D());
}
public static void M(B t)
{
// Works fine!
if (t is D)
((D)t).M();
}
public static void M<T>(T t) where T : B
{
// Compile error!
if (t is D)
((D)t).M();
}
}
在第二个示例中,您可以更改
((D)t).M();
自
((D)((B)t)).M();
你可以从B
投射到D
,但你不一定能从"B
的东西"投射到D
。B
的东西可以是A
,例如,如果A : B
。
当您可能在层次结构中从一个子级跳转到另一个子级时,就会出现编译器错误。可以上下转换层次结构,但不能跨层次结构进行转换。
((D)t).M(); // potentially across, if t is an A
((D)((B)t)).M(); // first up the hierarchy, then back down
另请注意,如果 t
实际上不是D
,您可能仍会收到运行时错误 ((D)((B)t)).M();
。(不过,您的is
检查应该可以防止这种情况。
(另请注意,在这两种情况下,编译器都不会考虑if (t is D)
检查。
最后一个例子:
class Base { }
class A : Base { }
class C : Base { }
...
A a = new A();
C c1 = (C)a; // compiler error
C c2 = (C)((Base)a); // no compiler error, but a runtime error (and a resharper warning)
// the same is true for 'as'
C c3 = a as C; // compiler error
C c4 = (a as Base) as C; // no compiler error, but always evaluates to null (and a resharper warning)
将其更改为
public static void M<T>(T t) where T : D
如果要强制要求 T 必须是类型 D,则这是适当的限制。
如果您的限制将 T 定义为 T 的 D,则它不会编译。
例如:你不能投List<int> to int
,反之亦然
您的模板函数有一个约束,需要T
才能B<T>
。
因此,当您的编译器尝试将类型为 T
的对象t
转换为 D
时,它无法执行此操作。因为T
保证是B<T>
,但不是D
。
如果添加约束以要求T
D
,这将起作用。 where T: B<T>, D
或简单地where T: D
这也保证了T
是B<T>
的,因为继承链。
问题的第二部分:当你调用t as D
时,它会在运行时进行检查。而且,在运行时,使用多态性验证t
可以转换为D
类型,并且没有错误。
注意:顺便说一下,这段代码太奇怪了。你确定自己在做什么吗?