簡體   English   中英

在解析動態對象時,C#是否為var選擇了錯誤的類型?

[英]Does C# pick the wrong type for var when parsing a dynamic object?

我使用以下代碼將一些Json轉換為動態對象。 當我在動態類型的屬性上使用DateTime.Parse時,我希望var猜測它的類型是DateTime ......相反,它保持動態。 這不可能是對的,可以嗎?

完整的例子如下。

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

var startDate = DateTime.Parse(settings.startDate);
var endDate = DateTime.Parse(settings.endDate);
var userId = int.Parse(settings.userId);

startDate,endDate和userId都是動態的,這意味着我不能在以后的Lambda表達式中使用它們。 顯然,我可以用以下代碼修復代碼:

DateTime startDate = DateTime.Parse(settings.startDate);
DateTime endDate = DateTime.Parse(settings.endDate);
int userId = int.Parse(settings.userId);

..但似乎編譯器正在做出“糟糕的猜測”。 任何人都可以向我解釋這個嗎?

謝謝

..但似乎編譯器正在做出“糟糕的猜測”。 任何人都可以向我解釋這個嗎?

當您使用dynamic ,整個表達式在編譯時被視為動態表達式 ,這會導致編譯器將所有內容視為動態並獲取運行時綁定。

這在C#語言規范的7.2中有解釋:

當不涉及動態表達式時,C#默認為靜態綁定,這意味着在選擇過程中使用組成表達式的編譯時類型。 但是,當上面列出的操作中的一個組成表達式是動態表達式時,操作將被動態綁定。

這基本上意味着大多數操作(類型在規范的7.2節中列出)具有任何聲明為dynamic元素將被評估為dynamic ,結果將是dynamic

在您的情況下,此聲明:

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

使用動態,因此,它被視為動態表達式。 由於“方法調用”是受綁定(7.2)的C#操作之一,因此編譯器將其視為動態綁定,這會使其評估為:

dynamic settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

反過來,這會導致DateTime.Parse表達式是動態綁定的,這反過來使它們返回dynamic

當你執行DateTime startDate = DateTime.Parse(settings.startDate);時,你的“修復”有效DateTime startDate = DateTime.Parse(settings.startDate); 因為這會強制將DateTime.Parse方法的結果隱式動態轉換(在規范的第6.1.8節中描述)到DateTime:

從dynamic類型的表達式到任何類型T都存在隱式動態轉換。轉換是動態綁定的(第7.2.2節),這意味着將在運行時從表達式的運行時類型中搜索隱式轉換如果沒有找到轉換,則拋出運行時異常。

在這種情況下,轉換是有效的,因此您可以有效地將所有內容切換回靜態綁定。

我不認為這特別令人驚訝。

DateTime.Parse(<dynamic>)將評估為動態。

DateTime startDate = <dynamic>執行從動態到DateTime的運行時分配。

你剛剛結合了這兩個。

編譯器不會猜測DateTime.Parse(<dynamic>)的類型不是動態的,但是它很聰明地意識到如果你將這個值賦值給DateTime然后假設它成功了你就是留下了DateTime。

這符合規范。 見§7.6.5:

如果至少滿足下列條件之一,則動態綁定調用表達式(第7.2.2節):

primary-expression具有編譯時類型dynamic

•可選參數列表的至少一個參數具有編譯時類型dynamic ,而primary-expression沒有委托類型。

考慮這種情況:

class Foo {
    public int M(string s) { return 0; }
    public string M(int s) { return String.Empty; }
}

Foo foo = new Foo();
dynamic d = // something dynamic
var m = foo.M(d);

什么應該是m的編譯時類型? 編譯器無法分辨,因為直到運行時才會知道調用Foo.M重載。 因此,它說m是動態的。

現在,您可以說它應該能夠找出DateTime.Parse只有一個重載,即使它沒有,但它的所有重載都具有相同的返回類型,在這種情況下它應該能夠找出編譯時類型。 這將是一個公平的觀點,也許是最好的Eric Lippert。 我問了一個單獨的問題來深入了解這個問題: 為什么方法調用表達式具有動態類型,即使只有一種可能的返回類型?

這里有兩個不同的概念

  1. dynamic :在運行時解析的任何類型
  2. var :這是在編譯時執行的隱式靜態類型

所以,如果你這樣做

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
var startDate = DateTime.Parse(settings.startDate);

在編譯時,它被解析為動態類型,並在運行時將其解析為具體類型。 編譯器檢查正確的部分new JavaScriptSerializer().Deserialize<dynamic>(json); ,返回動態。 在編譯時,這指示編譯器刪除所有類型安全檢查並將其保持到運行時。

這段代碼

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
DateTime startDate = DateTime.Parse(settings.startDate);

顯式地說動態對象是具體類型,因此編譯器能夠推斷出類型及其所有方法,並且能夠在編譯時執行靜態類型檢查。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM