2011-11-17 36 views
9

可能重复:
Compile-time and runtime casting c#为什么不C#编译器赶上一个InvalidCastException

据我了解,下面的代码将总是编译,并且将另外总是通过投掷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 
    } 
} 

我的问题是...

  1. 如果我的假设是关闭,然后有人可以提供一个例子演示一下怎么了?
  2. 如果我的假设是正确的,那么编译器为什么不警告这个错误?
+4

为什么你期望编译器遵循所有可能的代码路径来确定'p '在演员演出之前没有改变过? –

+0

如果事件没有改变,Post可以实现一个隐式操作符来将自己投射到Question上,反之亦然。 – PVitt

+4

演员是牛仔,上公牛和骑宝宝 – kenny

回答

14

为什么有这种转换是允许有两个原因。

首先,正如人们在其他答案中所说的那样,演员操作员的意思是“我比你知道得多;我向你保证,这种转换会成功,如果我错了,抛出异常并使程序崩溃”。如果你对编译器说谎,坏事将会发生;你实际上是而不是作出这样的保证,而程序因此崩溃。

现在,如果编译器可以告诉你说谎,那么它可以抓住你的谎言。编译器不需要任意巧妙地捕捉你的谎言!需要进行流分析以确定类型Base的表达式是否为从不将变为Derived类型是复杂的;比我们已经实现的逻辑复杂得多,比如未分配的本地变量。我们有更好的方式来花费我们的时间和精力,而不是提高编译器在明显的谎言中发现你的能力。因此

编译器通常原因,只有大约类型的表达式,不是关于可能值。仅仅从类型分析中就不可能知道转换是否成功。它可能成功,所以它是允许的。唯一不允许的转换是编译器知道的将从类型分析总是失败

二是可能(Derived)(new Base())其中派生是实现型基和具有它失败在运行时类型。在运行时,(Base)(new Base())也有可能失效并产生无效的转换异常!真实的事实!这些情况非常罕见,但他们可能是

有关详细信息,请参阅关于这个问题我的文章:

http://blogs.msdn.com/b/ericlippert/archive/2007/04/16/chained-user-defined-explicit-conversions-in-c.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/04/18/chained-user-defined-explicit-conversions-in-c-part-two.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/04/20/chained-user-defined-explicit-conversions-in-c-part-three.aspx

11

A Post在某些情况下可以投射到Question。通过执行演员表,你告诉编译器:“这会工作,我保证,如果没有,你可以抛出一个无效的演员例外。”

例如,该代码将很好地工作:

Post p = new Question(); 
    Question q = (Question)p; 

一铸造明确指出,你知道的比编译器是什么这实际上是更好的。你可能想要做一些类似asis的关键字?

+0

在我的回答中添加香蕉让我失去了宝贵的时刻,而且你的速度更快了。从我+1。 –

+0

@PaoloTedesco我喜欢你的香蕉示例。 – McKay

6

当你做一个明确的转换时,你告诉编译器“我知道你不知道”。

你在本质上重写编译器的正常逻辑 - p可能Question(因此,编译器将编译),你是在告诉你知道它(即使它不是”编译器t,因此运行时异常)。

8

问题是p可能是Question,因为问题从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 
    } 
} 
2

1)你的假设是关闭的。有人总是可以实现一个显式转换运营商的问题,从邮政转换:

public class Question` 
{ 
    // some class implementation 

    public static explicit operator Question(Post p) 
    { 
     return new Question { Text = p.PostText }; 
    } 
} 

2)有明确的转换是告诉你,你知道比它的编译器的方式。如果您不确定演员是否成功,并且不想要运行时异常时要使用某些内容,请使用isas运算符。

+0

你不会得到“不允许用户自定义转换到基类或从基类转换”吗? –

+0

问题的作者已经说明了类的内容(空),所以这个答案是无效的。他甚至没有向他们展示部分或任何东西,所以,“严格地说”,这个不适用。 – Meligy

+0

@MohamedMeligy - OP可能已经展示了他的实现......但编译器并不关心。仍然存在显式转换操作存在并且编译器不会检查的可能性。 –

0

你的假设是正确的:它会编译并且在运行时会失败。

在你的小例子中,很明显cast会失败,但编译器无法知道这一点。由于PostQuestion的超类型,因此您可以将Question指定为p,并且由于您进行了演员,因此您确实声明愿意承担编译器的某些责任。如果你一直试图分配一个string或其他不属于同一继承分支的东西,编译器应该警告你。相反,您可以尝试将object转换为任何类型。

但让编译器抱怨你的具体例子将意味着不允许任何强制转换。

1

编译器将p视为变量,因此它不会尝试跟踪它的值。如果确实如此,分析整个应用程序需要很长时间。一些静态分析工具的功能与FxCop相似。

编译器看到一个Post,但它并没有跟踪任务,它知道,也许:

Post p = new Question(); 

所以,它传递正常。

你知道你不能做的:

Question q = p; 

所不同的是在这一个你想告诉编译器使用它知道要验证这一点,它知道Post不一定是Question

在原始版本中,你告诉编译器“我知道它是这样的,我会明确地设置它,让我的方式,我会例外,如果我知道是错误的”,所以,它听取你和你的方式!

0

哇杰里米,我最近遇到了这个确切的问题!所以我做了这个方便的扩展方法,它映射了两个共享几个相同属性的模型。当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; 
    } 
} 

仅供参考,如果两个对象存在于同一个组件中,它可能只会有效。

大部分代码都来自这里:Mapping business Objects and Entity Object with reflection c#