为什么';C#编译器捕获到一个InvalidCastException
可能重复:为什么';C#编译器捕获到一个InvalidCastException,c#,exception,compiler-construction,compilation,.net,C#,Exception,Compiler Construction,Compilation,.net,可能重复: 据我所知,以下代码将始终编译,并且在运行时通过抛出InvalidCastException而另外始终失败 例如: public class Post { } public class Question : Post { } public class Answer : Post { public void Fail() { Post p = new Post(); Question q = (Question)p; // This
据我所知,以下代码将始终编译,并且在运行时通过抛出
InvalidCastException
而另外始终失败
例如:
public class Post { }
public class Question : Post { }
public class Answer : Post
{
public void Fail()
{
Post p = new Post();
Question q = (Question)p; // This will throw an InvalidCastException
}
}
我的问题是
在某些情况下,
Post
可以转换为问题
。通过执行强制转换,您告诉编译器,“我保证,这将起作用。如果不起作用,您可以抛出无效的强制转换异常。”
例如,此代码可以正常工作:
Post p = new Question();
Question q = (Question)p;
强制转换是明确声明您比编译器更清楚这实际上是什么。您可能希望执行类似于
as
或is
关键字的操作?当您执行显式转换时,您告诉编译器“我知道一些您不知道的事情”
实际上,您正在重写编译器的正常逻辑-
p
可能是一个问题(因此,编译器将编译),您告诉编译器您知道它是(即使它不是,因此运行时异常)。关键是p
可能是一个问题,问题继承自Post
考虑以下事项:
public class Post { }
public class Question : Post { }
public class Banana { }
static class Program {
public static void Main(params string[] args) {
Post p = new Question();
Question q = (Question)p; // p IS a Question in this case
Banana b = (Banana)p; // this does not compile
}
}
1) 你的假设是错误的。有些人总是可以为要从Post转换的问题实现显式转换运算符:
public class Question`
{
// some class implementation
public static explicit operator Question(Post p)
{
return new Question { Text = p.PostText };
}
}
2) 显式强制转换是告诉编译器您比它更了解的一种方式。如果您不确定强制转换是否成功且不希望出现运行时异常时需要使用某些内容,请使用is
和作为
运算符。您的假设是正确的:它将编译,并在运行时失败
在您的小示例中,很明显强制转换将失败,但编译器无法知道这一点。由于Post
是Question
的超类型,您可以将Question
分配给p
,并且由于您进行了转换,因此您声明愿意承担编译器的一些责任。如果您试图分配一个字符串
或其他不属于同一继承分支的内容,编译器应该警告您。相反,您始终可以尝试将对象
强制转换为任何类型
但是让编译器抱怨您的特定示例意味着不允许强制转换 编译器将p视为一个变量,因此它不会尝试跟踪它的值。如果是这样的话,分析整个应用程序就需要很长时间。一些静态分析工具像FxCop
编译器看到一个Post
,但它没有跟踪赋值,并且它知道可能:
Post p = new Question();
所以,它正常地通过它
你知道你不能做:
Question q = p;
区别在于,您试图告诉编译器使用它所知道的来验证这一点,并且它知道Post
不一定是问题
在原始版本中,您告诉编译器“我知道是这样的,我会明确地设置它,滚开,如果我知道的是错误的,我会接受例外”,因此,它会听您的,滚开 哇,杰里米,我最近遇到了这个问题!所以我制作了这个方便的扩展方法,它映射了两个共享一些相同属性的模型。其目的是在类A从类B继承时使用它来将类B映射到类A。希望您发现它有帮助
public static class ObjectHelper
{
public static T Cast<T>(this Object source)
{
var destination = (T)Activator.CreateInstance(typeof(T));
var sourcetype = source.GetType();
var destinationtype = destination.GetType();
var sourceProperties = sourcetype.GetProperties();
var destionationProperties = destinationtype.GetProperties();
var commonproperties = from sp in sourceProperties
join dp in destionationProperties on new { sp.Name, sp.PropertyType } equals
new { dp.Name, dp.PropertyType }
select new { sp, dp };
foreach (var match in commonproperties)
{
match.dp.SetValue(destination, match.sp.GetValue(source, null), null);
}
return destination;
}
}
公共静态类ObjectHelper
{
公共静态T转换(此对象源)
{
var destination=(T)Activator.CreateInstance(typeof(T));
var sourcetype=source.GetType();
var destinationtype=destination.GetType();
var sourceProperties=sourcetype.GetProperties();
var destinationproperties=destinationtype.GetProperties();
var commonproperties=来自sourceProperties中的sp
在新的{sp.Name,sp.PropertyType}equals上的DestinationProperties中加入dp
新的{dp.Name,dp.PropertyType}
选择新的{sp,dp};
foreach(commonproperties中的变量匹配)
{
match.dp.SetValue(目标,match.sp.GetValue(源,null),null);
}
返回目的地;
}
}
仅供参考,它可能仅在两个对象存在于同一部件中时才起作用
大部分代码来自这里:允许这种转换的原因有两个
首先,正如人们在其他答案中所说,cast操作符的意思是“我知道的比你多;我向你保证,这种转换将成功,如果我错了,抛出一个异常并使过程崩溃”。如果你对编译器撒谎,坏事就会发生;事实上,你没有做出保证,因此程序正在崩溃
现在,如果编译器能告诉你在对它撒谎,那么它就能抓住你的谎言。编译器不需要任意聪明地抓住你的谎言!确定Base类型的表达式永远不会是派生类型所需的流分析是复杂的;要比我们已经实现的逻辑复杂得多,以捕获未分配的局部变量之类的内容。我们有更好的方法来花费我们的时间和精力,而不是提高编译器的能力,让你在明显的谎言中被识破
因此,编译器通常只考虑表达式的类型,而不考虑可能的值。仅从类型分析不可能知道转换是否会成功。它可能会成功,因此是允许的。唯一不允许的强制转换是编译器知道将要执行的强制转换