簡體   English   中英

為什么不將違反方案委托給價值類型?

[英]Why doesn't delegate contravariance work with value types?

此代碼段未在LINQPad中編譯。

void Main()
{
    (new[]{0,1,2,3}).Where(IsNull).Dump();
}

static bool IsNull(object arg) { return arg == null; }

編譯器的錯誤消息是:

'UserQuery.IsNull(object)'沒有重載匹配委托'System.Func'

它適用於字符串數組,但不適用於int[] 這顯然與拳擊有關,但我想知道細節。

給出的答案(沒有涉及值類型的差異)是正確的。 當其中一個變化類型參數是值類型時協方差和逆變不起作用的原因如下。 假設它確實有效並且顯示出事情是如何發生可怕錯誤的:

Func<int> f1 = ()=>123;
Func<object> f2 = f1; // Suppose this were legal.
object ob = f2();

好的,會發生什么? f2與f1的引用相同。 因此無論f1做什么,f2都可以。 f1做什么? 它在堆棧上放置一個32位整數。 作業有什么作用? 它接受堆棧中的任何內容並將其存儲在變量“ob”中。

拳擊教學在哪里? 沒有人! 我們只是將32位整數存儲到期望不是整數的存儲中,而是存儲到包含盒裝整數的堆位置的64位指針。 因此,您只是錯誤地對齊堆棧並使用無效引用損壞變量的內容。 很快這個過程就會火上澆油。

那么拳擊教學應該去哪里? 編譯器必須在某處生成裝箱指令。 它不能在調用f2之后去,因為編譯器認為f2返回一個已經裝箱的對象。 它不能調用f1,因為f1返回一個int,而不是一個盒裝的int。 它不能在f2的調用和f1的調用之間進行, 因為它們是同一個委托; 沒有'之間'

我們唯一能做的就是讓第二行真正意味着:

Func<object> f2 = ()=>(object)f1();

現在我們不再在f1和f2之間有參考標識,那么方差點是什么? 協變參考轉換的重點保留參考標識

無論你如何切片,事情都會出現嚴重錯誤,無法解決問題。 因此,最好的辦法是首先使該功能非法; 泛型委托類型不允許存在差異,其中值類型將是變化的東西。

更新:我應該在我的回答中注意到,在VB中,您可以將一個返回int的委托轉換為一個返回對象的委托。 VB只生成第二個委托,它將調用包裝到第一個委托並將結果打包。 VB選擇放棄引用轉換保留對象標識的限制。

這說明了C#和VB的設計理念的一個有趣的區別。 在C#中,設計團隊一直在思考“編譯器如何找到可能是用戶程序中的錯誤並引起他們注意?” VB團隊正在思考“我們怎樣才能弄清楚用戶可能意味着什么,並代表他們這樣做?” 簡而言之,C#哲學是“如果你看到某些東西,說些什么”,那么VB哲學就是“做我的意思,而不是我所說的”。 兩者都是完全合理的哲學; 有趣的是,由於設計原則,兩種具有幾乎相同特征集的語言在這些小細節中是如何不同的。

因為Int32是值類型而反對方差不適用於值類型。

你可以嘗試這個:

(new **object**[]{0,1,2,3}).Where(IsNull).Dump();

它不適用於int,因為沒有對象。

嘗試:

void Fun()
{
    IEnumerable<object> objects = (new object[] { 0, 1, null, 3 }).Where(IsNull);

    foreach (object item in objects)
    {
        Console.WriteLine("item is null");
    }
}

bool IsNull(object arg) { return arg == null; }

暫無
暫無

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

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