2017-08-27 46 views
3

讓我們假設我們有兩個不相關的接口約束擴展方法產生ambigious呼叫

public interface IFirst 
{ 

} 

public interface ISecond 
{ 

} 

而且具有相同名稱的擴展方法,但受限於每個接口。

public static class IFirstExtensions 
{ 
    public static void DoIt<TFirst>(this TFirst model) 
     where TFirst : IFirst 
    { 

    } 
} 

public static class ISecondExtensions 
{ 
    public static void DoIt<TSecond>(this TSecond model) 
     where TSecond : ISecond 
    { 

    } 
} 

當我試圖用IFirst實例的工作:

IFirst first = ...; 
first.DoIt(); 

然後我得到錯誤:

"CS0121 The call is ambiguous between the following methods or properties: 'IFirstExtensions.DoIt(TFirst)' and 'ISecondExtensions.DoIt(TSecond)'".

這很奇怪。看起來這兩種方法在這個範圍內都是可見的。但是,如果我不同他們的名字,如:

public static class IFirstExtensions 
{ 
    public static void DoFirst<TFirst>(this TFirst model) 
     where TFirst : IFirst 
    { 

    } 
} 

public static class ISecondExtensions 
{ 
    public static void DoSecond<TSecond>(this TSecond model) 
     where TSecond : ISecond 
    { 

    } 
} 

然後約束的作品,第二種方法是不可見的募集編譯錯誤:檢測

IFirst first = ...; 
first.DoSecond(); 

所以看起來像這樣的約束條件staisfying作品differenly含糊不清以及打電話時。但在C#規範中,我發現只有一篇與本主題相關的章節嚴格描述了約束是如何工作的。這是編譯器中的錯誤還是我錯過了一些東西?

回答

8

通用約束條件是不是方法簽名的一部分。這兩種方法在重載分辨率方面都完全相同,因此會產生一個模糊的調用錯誤。

Specifically, the signature of a method consists of its name, the number of type parameters and the number, modifiers, and types of its formal parameters (C# 5.0 Specification, 10.6 Methods)

For the purposes of signature comparisons any type-parameter-constraints-clauses are ignored, as are the names of the method’s type-parameters, but the number of generic type parameters is relevant (ECMA-334, 25.6.1 Generic method signatures)

說得更加清楚,當涉及到重載,既擴展方法是簡單的:

public static void DoFirst<T>(this T model) 

另外,請注意,這個問題並不僅僅涉及到擴展方法。請看下面的例子,其中具有相同簽名但不同的約束兩個通用方法相同的類內聲明的是:

class Foo 
{ 
    void Bar<T>(Blah blah) where T: Frob { } 
    void Bar<T>(Blah blah) where T: Blob { } //CS0111 error 
} 

你會得到一個編譯時錯誤:

CS0111 Type 'Foo' already defines a member called 'Bar' with the same parameter types.

+0

Foo類從使用不同我講過的無關課程。我會看到我的版本被關閉成簽名,如:Bar(Frob blah),Bar(Blob blah)。這就是爲什麼而不是CS0111編譯沒有問題。如果你看一下MethodInfo-> Signature-> Arguments [0],你會看到在ImplementedInterfaces屬性中具有不同值的絕對不同的類型。所以在運行時簽名是不同的,它包含約束。 – Stan

+0

但你說得對。我錯過了規範中的一點,通用約束官方忽略簽名。這很荒謬。如果我將擴展IFirst/ISecond Inrerfaces - 這沒關係。如果我將這些合同作爲約束 - 事實並非如此。 – Stan

+0

@stan不,簽名是相同的,錯誤是不同的,因爲方法是在不同的類中定義的。當解決呼叫時,兩種方法都在候選集合中,並且這是當識別簽名發出莊稼並且呼叫被標記爲不明確的時刻。之前沒有給出該錯誤,因爲當將其作爲常規靜態方法調用時,您總是可以明確定義實現擴展方法的靜態類。對於實現兩個具有相同方法簽名和兩個不同約束的接口的類,情況也是如此。 – InBetween