簡體   English   中英

接口中的類型約束適用於基類

[英]Type constraints in interface apply to base class

我有一個基類定義了這樣的泛型方法:

public class BaseClass
{
    public T DoSomething<T> ()
    { ... }
}

由於這個類是由第三方提供的,並沒有附帶接口,因此我定義了一個接口,用於定義該類中實際需要的方法。 這樣我就可以得到松散的耦合,並且實際上可以用其他東西來交換第三方類。 對於此示例,請考慮以下接口:

public interface ISomething
{
    T DoSomething<T> ()
        where T : Foo;
}

如您所見,它定義了相同的方法,但也對類型參數應用了類型約束,該類型參數來自與此無關的其他一些要求。

接下來,我定義了BaseClass的子類型,它也實現了ISomething 該類將用作接口背后的通常實現 - 而接口將是應用程序的其余部分將訪問的內容。

public class Something : BaseClass, ISomething
{
    // ...
}

由於BaseClassDoSomething已經支持任何類型參數T ,它應該特別支持一個類型參數,它是Foo的子類型。 因此可以預期BaseClass的子類型已經實現了接口。 但是我收到以下錯誤:

方法'BaseClass.DoSomething()'的類型參數'T'的約束必須匹配接口方法'ISomething.DoSomething()'的類型參數'T'的約束。 請考慮使用顯式接口實現。

現在,我有兩種可能性; 第一個是做錯誤建議並明確地實現接口。 第二種是使用new隱藏基本實現:

// Explicit implementation
T ISomething.DoSomething<T> ()
{
    return base.DoSomething<T>();
}

// Method hiding
public new T DoSomething<T>()
    where T : Foo
{
    return base.DoSomething<T>();
}

兩者都有效,雖然我可能更喜歡第二種解決方案,以保持方法可以從類本身訪問。 但是它仍然留下以下問題:

當基類型使用less-strict(read:none)類型約束實現它時,為什么必須重新實現該方法? 為什么該方法需要完全按原樣實現?

編輯:為了給方法更多的意義,我將返回類型從void更改為T 在我的實際應用程序中,我有泛型參數和返回值。

嘗試使用組合而不是繼承來實現Something

public class Something : ISomething
{
    private readonly BaseClass inner = ...;

    void DoSomething<T>() where T : Foo
    {
        inner.DoSomething<T>();
    }
}

當然,給定的代碼可以編譯並安全運行:

Something實例被輸入SomethingBaseClass ,編譯器將允許T任何類型,而當同一實例被輸入為ISomething ,它將只允許繼承Foo類型。 在這兩種情況下,您都可以照常進行靜態檢查和運行時安

實際上,上面的場景正是您明確實現ISomething時會發生的情況。 那么讓我們看看我們可以為當前的事態做出什么樣的爭論。

對於:

  • 擬議的解決方案不適用於所有情況; 它取決於確切的方法簽名(類型參數協變?逆變?不變?)
  • 它不要求用新文本修改規范,說明如何處理這些案件
  • 它使代碼自我記錄 - 你不必學習所說的文本; 關於顯式接口實現的當前規則就足夠了
  • 它不會對C#編譯器團隊施加開發成本(文檔,功能實現,測試等)

反對:

  • 你需要輸入更多

考慮到上述情況以及此事實並非日常情況,恕我直言會得出的結論很明確:這可能會很好,但它肯定不會讓您不顧一切地實施它。

您可以使用下面的代碼獲得所需內容。 通過在接口defenition中包含type參數,您可以使其協變,這似乎滿足編譯器。 Base類保持不變,您可以使用單個方法隱藏Base實現並實現接口。

class Program
{
    static void Main()
    {
        var something = new Something<Foo>();
        var baseClass = (BaseClass)something;
        var isomething = (ISomething<Foo>)something;

        var baseResult = baseClass.DoSomething<Bar>();
        var interfaceResult = isomething.DoSomething<Bar>();
        var result = something.DoSomething<Bar>();
    }
}

class Foo 
{
}

class Bar : Foo
{
}

class BaseClass
{
    public T DoSomething<T>()
    {
        return default(T);
    }
}

interface ISomething<out T> where T : Foo
{
    T DoSomething<T>();
}

class Something<T> : BaseClass, ISomething<T> where T : Foo
{
    public new T DoSomething<T>()
    {
        return default(T);
    }
}

或者如果你真的不想在實例化中指定Foo

class Program
{
    static void Main()
    {
        var something = new Something();
        var baseClass = (BaseClass)something;
        var isomething = (ISomething)something;

        var baseResult = baseClass.DoSomething<Bar>();
        var interfaceResult = isomething.DoSomething<Bar>();
        var result = something.DoSomething<Bar>();
    }
}

class Foo 
{
}

class Bar : Foo
{
}

class BaseClass
{
    public T DoSomething<T>()
    {
        return default(T);
    }
}

interface ISomething
{
    T DoSomething<T>;
}

interface ISomething<S> : ISomething where S : Foo
{
    new R DoSomething<R>() where R : Foo;
}

class Something : BaseClass, ISomething
{
    public new T DoSomething<T>()
    {
        return default(T);
    }
}

暫無
暫無

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

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