簡體   English   中英

有關C#語言規范中隱式轉換的問題

[英]Question regarding implicit conversions in the C# language specification

6.1節隱式 轉換因此定義了身份轉換

身份轉換從任何類型轉換為相同類型。 存在這種轉換,使得已經具有所需類型的實體可以說可轉換為該類型。

現在,這些句子的目的是什么?

(在§6.1.6隱式參考轉換中)

隱式引用轉換是:

  • [...]
  • 從任何引用類型引用類型 T如果它具有對引用類型 T 0的隱式標識或引用轉換,則T 0具有到T的標識轉換。

和:

(在§6.1.7拳擊轉換中)

  • 如果值類型具有到接口類型I 0的裝箱轉換並且I 0具有到I的標識轉換,則值類型具有到接口類型I的裝箱轉換。

最初它們似乎是多余的(同義詞)。 但他們必須出於某種目的,所以為什么他們在那里?

你能給出一個T 1T 2這兩種類型的例子,如果不是上面引用的段落, T 1不能隱式轉換為T 2嗎?

2010年9月22日更新:

我懷疑除了蒂姆維之外還有人會讀這個。 即便如此,我想對這個答案進行一些編輯,因為現在已經接受了一個新的答案,並且辯論仍在繼續(至少在我想象的世界中)是否引用了規范的摘錄在技​​術上是多余的。 我並沒有增加太多,但它太大了,不適合評論。 大部分更新可以在下面的“涉及dynamic類型的轉換 ”標題下找到。


2010年9月19日更新:

在你的評論中:

[T]他沒有意義。

該死的,蒂姆維,你說的很多 但是好吧,那么; 你讓我處於守勢,所以來吧!

免責聲明:我絕對沒有像你一樣仔細檢查規格。 基於你最近的一些問題,你好像最近一直在研究它。 這自然會讓你比SO上的大多數用戶更熟悉很多細節; 所以,就像你從Eric Lippert以外的任何人那里收到的大多數答案一樣,這可能不會讓你滿意。

不同的前提

首先,你的問題的前提是,如果突出顯示的陳述是多余的 ,那么它們就沒有用處 我的答案的前提是,如果多余的陳述澄清了對每個人都不明顯的東西,那么多余的陳述並不一定沒有目的。 這些是相互矛盾的前提。 如果我們不能就前提達成一致,我們就不能有一個直截了當的邏輯論證。 我只是想讓你重新思考你的前提。

然而,你的回答是重申你的前提:“如果句子真的是多余的,那么他們只會混淆讀者而不澄清任何事情。”

(順便說一句,我喜歡你如何讓自己成為所有規范讀者的代表。)

我完全不能責怪你擔任這個職位。 我的意思是,這似乎顯而易見。 我的原始答案中沒有給出任何具體的例子。 所以下面我將嘗試包括一些具體的例子。 但首先,讓我退后一步,提出我為什么這個奇怪的身份轉換概念首先存在於規范中。

身份轉換定義的目的

乍一看,這個定義似乎是多余的; 是不是只是說任何類型T的實例都可以轉換為......好吧,到T? 是的。 但我假設*這個定義的目的是為規范提供適當的詞匯,以便在討論轉換的過程中利用類型身份的概念。

這允許有關轉換的陳述本質上是可傳遞的。 您從規范中引用的第一點作為重言式聲明的一個例子屬於這一類。 它表示如果為某種類型(我將其稱之為K)定義了隱式轉換為另一種類型T 0並且T 0 具有到 T 的標識轉換 ,那么K可以隱式轉換為T.通過給定標識轉換的定義上面,“有一個身份轉換為”真正意味着“是同一類型。” 所以聲明是多余的

但同樣重要的是: 身份轉換定義首先存在於為規范配備描述轉換的形式語言,而不必說“如果T 0和T真的是同一類型”。

好的,具體例子的時間。

對於某些開發人員而言,隱式轉換的存在可能並不明顯

注意:Eric Lippert在回答這個問題時提供了一個更好的例子。 我把這前兩個例子留作我的觀點的一點點補充。 我還添加了第三個例子,它將objectdynamic之間存在的身份轉換具體化,如Eric的回答所指出的那樣。

傳遞參考轉換

假設您有兩種類型, MN ,並且您有一個如下定義的隱式轉換:

public static implicit operator M(N n);

然后你可以寫這樣的代碼:

N n = new N();
M m = n;

現在讓我們說你有一個帶有這個using語句的文件:

using K = M;

然后你在文件的后面有:

N n = new N();
K k = n;

好的,在我繼續之前,我意識到這對 我來說是顯而易見的。 但我的回答是,從一開始就是這樣,對每個人來說可能並不明顯,因此指明它 - 雖然多余 -仍然有目的

這樣做的目的是:為了清楚任何人抓撓他們的頭,看看那個代碼,這是合法的。 存在從N到M的隱式轉換 ,並且存在從M到K的身份轉換 (即,M和K是相同的類型); 因此存在從N到K的隱式轉換。它不僅僅是邏輯的(盡管它可能合乎邏輯的); 它就在規范中 否則,人們可能會錯誤地認為需要以下內容:

K k = (M)n;

顯然,事實並非如此。

傳遞拳擊轉換

或者采用int類型。 int可以裝箱為IComparable<int> ,對吧? 所以這是合法的:

int i = 10;
IComparable<int> x = i;

現在考慮一下:

int i = 10;
IComparable<System.Int32> x = i;

再次, 是的 ,對於您,我和90%可能遇到過它的開發人員來說,這可能是顯而易見的 但對於那些看不到它的苗條少數人來說:從intIComparable<int>存在一個拳擊轉換 ,並且存在從IComparable<int>IComparable<System.Int32>身份轉換 (即IComparable<int>IComparable<System.Int32>是相同的類型); 所以從intIComparable<System.Int32>存在一個裝箱轉換。

涉及dynamic類型的轉換

我將借用上面的參考轉換示例,稍微調整一下,以說明規范4.0版本中objectdynamic之間的身份關系。

假設我們有類型M<T>N ,並且在某處定義了以下隱式轉換:

public static implicit operator M<object>(N n);

那么以下是合法的:

N n = new N();
M<dynamic> m = n;

顯然,上述內容遠不如前兩個例子明顯 但這是一個百萬美元的問題: 即使問題中引用的規范摘錄不存在,上述內容仍然合法嗎? (為簡潔起見,我打算將這些摘錄稱為Q。 )如果答案是肯定的,那么Q實際上是多余的。 如果不是,那就不是。

我相信答案是肯定的。

考慮6.1.1節中定義的身份轉換的定義(我在這里包括整個部分,因為它很短):

身份轉換從任何類型轉換為相同類型。 存在這種轉換,使得已經具有所需類型的實體可以說可轉換為該類型。

因為objectdynamic被認為是等價的,所以在objectdynamic之間存在身份轉換, 並且在用object替換所有dynamic出現時構造的類型之間是相同的 [強調我的]

(最后一部分也包含在4.7節中,它定義了dynamic類型。)

現在讓我們再看看代碼。 特別是我對這一行感興趣:

M<dynamic> m = n;

這句話的合法性(無視Q - 記住,正在討論的問題是如果 Q 存在則上述陳述的假設合法性),因為M<T>N是自定義類型,取決於用戶的存在 -定義NM<dynamic>之間的隱式轉換。

存在從NM<object>的隱式轉換。 根據上面引用的規范部分,在M<object>M<dynamic>之間存在身份轉換。 通過身份轉換的定義, M<object>M<dynamic> 是相同的類型

因此,就像在前兩個(更明顯的)示例中一樣,我認為即使不考慮Q存在從NM<dynamic>的隱式轉換,就像從N存在隱式轉換一樣。在第一個示例中為K ,並且在第二個示例中,從intIComparable<System.Int32>存在裝箱轉換。

沒有Q ,這就不那么明顯了(因此Q存在); 但這並不是假的 (即,定義此行為不需要 Q )。 它只是使它不那么明顯。

結論

我在原來的答案中說,這是“明顯的”解釋,因為在我看來,你正在吠叫錯誤的樹。 你最初提出了這個挑戰:

你能給出一個T 1 ,T 2這兩種類型的例子,如果不是上面引用的段落,T 1就不能隱式轉換為T 2嗎?

沒有人會遇到這個挑戰,蒂姆維,因為這是不可能的。 參考有關參考轉換的第一段摘錄。 假設類型K可以隱式轉換為類型T,如果它可以隱式轉換為T 0並且T 0與T相同。解構它,將它重新組合在一起,並且你留下了明顯的重言式:K如果它可以隱式轉換為T,則可以隱式轉換為T.這會引入任何新的隱式轉換嗎? 當然不是。

也許Ben Voigt的評論是正確的; 也許你所詢問的這些要點會更好地放在腳注中,而不是放在文本正文中。 在任何情況下,我清楚它們多余的,因此從這個前提開始它們不能是多余的,否則它們就不會有傻瓜的差事。 願意接受冗余的陳述可能仍然會對一個對每個人都不太明顯的概念有所了解,並且接受這些陳述將變得更容易。

冗余? 是。 同義反復? 是。 無意義? 看來,沒有。

*顯然,我沒有參與編寫C#語言規范。 如果我這樣做,這個答案會更具權威性。 事實上,它只是代表一個人試圖理解一個相當復雜的文件的微弱嘗試。


原始答案

我認為你(或許有意)忽略了這里最明顯的答案。

在你的問題中考慮這兩句話:

(1)最初它們似乎是多余的(同義詞)。 (2)但他們必須在某個目的,所以為什么他們在那里?

對我來說,這兩個句子的含義是一個同義詞的說法沒有任何意義。 但是,僅僅因為聲明遵循既定的前提邏輯,這並不是每個人都明白的。 換句話說,即使(1)為真,對(2)的答案可能只是: 使所描述的行為對於閱讀規范的任何人都清楚

現在你可能會爭辯說,即使某些東西不明顯 ,如果提供冗余定義,它仍然不屬於規范。 對於這種可能的反對意見,我只能說:做到現實。 在我看來,梳理一份文件,刪除所有只是陳述可以從先前陳述中推斷出的事實的陳述,這是不切實際的(在我看來)。

如果這一種常見的做法,我認為你會發現很多文獻 - 不只是規格,而是研究論文,文章,教科書等 - 會更短,更密集,更難理解。

所以:是的,也許它們是多余的。 但這並不能否定他們的目的。

規范的第4.7節指出存在從Foo<dynamic>Foo<object>的身份轉換,反之亦然。 您引用的規范部分是為了確保處理此案例而編寫的。 也就是說,如果存在從T到C<object, object>的隱式引用轉換,那么還存在對C<object, dynamic>C<dynamic, object>C<dynamic, dynamic>的隱式引用轉換。

有人可能會合理地指出:(1)這些短語的意圖並不明顯 - 因此你的問題 - 並且令人困惑,(2)關於身份轉換的部分應該交叉引用關於動態轉換的部分,以及(3)短語像規范中的這樣,規范的實現者很難將規范語言清楚地轉換為實現。 如何知道是否存在任何此類類型? 規范不需要指定精確的算法,但如果它提供更多的指導,那將是很好的。

遺憾的是,該規范並不是一份完美的文件。

身份轉換從任何類型轉換為相同類型 存在這種轉換, 使得已經具有所需類型的實體可以說可轉換為該類型。

這表示在C#-land中,1 == 1; 鍬是一個鍬。 這是將對象引用分配給相同類型的變量的基礎; 如果變量類型T1和一個類型T2實際上都是黑桃,則可以將一個分配給另一個,而不必將其中一個顯式轉換為Spade。 想象一下C#變體,其中賦值必須如下所示:

Spade mySpade = new Spade();
Spade mySpade2;

mySpade2 = (Spade)mySpade; //explicit cast required

此外,數學中的“同一性”表明,給定一組輸入的結果的表達式等價於另一個表達式,該表達式在給定相同輸入的情況下產生相同的結果。 在編程中,這意味着計算類型實例的表達式或函數等效於該類型,而不進行顯式轉換。 如果不成立,則需要以下代碼:

public int myMethod() { /*Do something*/ }
...
int myInt = (int)myMethod(); //required even though myMethod() evals to an int.
...
int myInt = (int)(1 + 2); //required even though 1, 2, and 1+2 eval to an int.

第二個規則基本上表示可以將值類型分配給類上的成員變量,如果部分變量(按定義為盒裝類型,因為其容器是引用類型)被聲明為相同類型。 如果未指定此規則,理論上可能存在一個C#版本,其中純值類型必須顯式轉換為其引用模擬,以便作為成員變量存儲在類中。 想象一下,例如,一個C#版本,其中blue關鍵字類型(int,float,decimal)和淺藍色類名稱(Int32,Float,Decimal)引用兩個非常不同的,只能顯式轉換的類型,以及int ,float,decimal等作為成員變量類型不合法,因為它們不是引用類型:

public class MyClass
{
  Int32 MyBoxedValueType; //using "int" not legal
}

...

MyClass myClass = new MyClass();
int theInt = 2;

myClass.MyBoxedValueType = (Int32)theInt; //explicit cast required

我知道這聽起來很愚蠢,但在某種程度上,這些東西必須是已知的,而在計算機中,你必須指定一切。 有時讀冰島美國曲棍球規則手冊; 本書的第一條規則是游戲應該在冰面上進行。 它是最終的“好戰”之一,但也是必須理解的游戲的基本事實,以便任何其他規則有意義。

可能是這樣,代碼在像Convert.ChangeType(client, typeof(Client))一樣被調用時保證傳遞Convert.ChangeType(client, typeof(Client))無論是否實現了IConvertible

從帶有Reflector的mscorlib查看ChangeType的源代碼,並注意按原樣返回value的條件。

記住a =運算符不是轉換,只是參考集。 因此, Client client_2 = client_1類的代碼不會執行任何隱式轉換。 如果聲明了標識隱式轉換,則發出錯誤CS0555

我想規范說讓C#編譯器處理這種情況,因此不要手動嘗試定義身份轉換。

暫無
暫無

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

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