2011-11-17 44 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#