簡體   English   中英

C#中的通用擴展方法:在這種邊緣情況下會發生什么?

[英]Generic extension methods in C#: what will happen in this edge case?

在我最近的一個問題中,我了解到如果有多個擴展方法具有與給定類型匹配的約束,則將選擇最具體的擴展方法。 這讓我思考 - 編譯器如何確定哪一個“更具體”? 結果會是什么?

假設我有以下課程:

public MyClass : IComparable, IDisposable
{
    // Implementation of members
}

public static class MyExtensions
{
    public static void DoSomething<T>(this T item)
        where T : IComparable
    { /* whatever */ }

    public static void DoSomething<T>(this T item)
        where T : IDisposable
    { /* whatever else */ }
}

如果我現在使用擴展方法

var instance = new MyClass();
instance.DoSomething();

將使用哪種方法? 或者編譯器會拋出錯誤?

注意:我不是說這是好設計,甚至我說我需要這樣做。 但是“更具體”這個詞足以讓我思考這一點,現在我必須知道! :P

更新:我想我是不是真的在上面的例子中會發生什么 ,在為什么感興趣。 因為我做過類似的事情,所以我想到了

public static class CollectionExtensions
{
    public static void DoSomething<T>(this T items) where T : IList { ... }
    public static void DoSomething<T>(this T items) where T : IEnumerable { ... }
}

編譯器知道為new List<Something>().DoSomething()選擇第一種方法,因為它與傳遞的類型“更接近”。 我當時感興趣的是“在這種情況下更接近的意思是什么?如果約束來自兩個不同的繼承鏈,編譯器將如何反應?為什么?”

在這種情況下,擴展類不會編譯 - 您不能僅基於泛型約束重載方法。

如果你把兩個擴展方法放到不同的類中,那么調用代碼就不會編譯 - 這將是一個模糊的調用,因為這兩種方法都不會比另一種方法“更好”...在這兩種情況下泛型類型的參數都是推斷為MyClass ,所以只有兩次從MyClassMyClass轉換,這兩種轉換都不比另一種好。

一旦您發現沒有適用的實例方法,這基本上只是一個特殊的重載情況。 我最近剛寫了一篇關於重載文章 ,你可能覺得它很有用 - 它沒有提到這個具體的情況,但是如果你想詳細看一下,它就指向規范的相關部分。

它根本不會編譯並拋出編譯時錯誤,說兩個方法之間的調用是不明確的。

類型'MyExtensions'已經定義了一個名為'DoSomething'的成員,它具有相同的參數類型。

編輯

這就是編譯器給出這樣的錯誤的原因。 擴展方法只是語法糖,它們所做的就是為任何類型帶來流暢性和可讀性。

檢查此代碼..

var instance = new MyClass();
instance.DoSomething();

編譯器將此代碼替換為以下內容。

var instance = new MyClass();
MyExtensions.DoSomething(instance);
//Compiler gets confused. Which one to call IComparable or IDisposable

在你的情況下編譯器會混淆,因為方法調用有兩個匹配的簽名,它會給你所說的錯誤。

通用約束不被視為方法簽名的一部分。 編譯器將這兩種方法視為具有相同簽名的方法。 所以你會得到編譯錯誤,說已經定義了DoSomething方法。

public static void DoSomething<T>(this T item)
    where T : IComparable
{ /* whatever */ }

public static void DoSomething<T>(this T item)
    where T : IDisposable
{ /* whatever else */ }

請考慮以下示例:

class MyClass {}

static class MyClassExtensions
{
    public static void DoSomething<T>(this T item, List<string> someList)
    {
        Console.WriteLine("Method with List in signature is called.");  
    }

    public static void DoSomething<T>(this T item, IEnumerable<string> someEnumerable)
    {
        Console.WriteLine("Method with IEnumerable in signature is called.");   
    }
}

在此示例中,使用以下內容進行測試時:

var myClass = new MyClass();
myClass.DoSomething(new List<string>());

調用擴展類中的第一個方法。 簡而言之,這意味着編譯器確定更接近傳遞的參數的簽名,並使用它。

暫無
暫無

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

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