簡體   English   中英

是否可以將泛型參數約束為當前對象的子類型?

[英]Is it possible to constrain a generic parameter to be a subtype of the current object?

這是我剛剛遇到的一個有趣的問題。 可以使用擴展方法做我想做的事情,但似乎不可能對類本身的成員做。

使用擴展方法,可以編寫一個具有如下簽名的方法:

public static void DoStuff<T>(this T arg1, T arg2)

這強制了兩個參數都是你想要調用它的任何類型。 與委托一起使用時,這會變得更有用。

public static void DoStuff<T>(this T arg1, Action<T> arg2)

但是,我無法與成員合作。 這沒有這樣的限制:

public void DoStuff<T>(T arg1) where T : typeof(this)

如果這確實有效,那么你可以像這樣在你的基類上定義一個方法(我使用了流,因為它們是.NET中內置的層次結構):

class Stream
{
    public void DoStuff<T>(T stream) where T : this
    {
    }
}

然后在子類上,不可能像這樣調用它:

ByteStream bs = new ByteStream()
bs.DoStuff(new Stream()) // Error! DoStuff() should be inferred as DoStuff<ByteStream>()

有沒有辦法做到這一點? 我相信自動從參數中推斷出類型,擴展方法是語法糖。 這可能就是它起作用的原因; 因為擴展方法被靜態調用替換,然后允許推斷類型。

我問,因為我試圖將擴展方法移動到一個公共基類,並且無法在不添加類型信息的情況下進行編譯。

澄清。 這不是僅添加where T : MyType的情況,因為如果我創建一個從MyType繼承的名為MySubType的類型,我將能夠在MySubType的實例上調用DoStuff並將MyType作為參數傳遞。 這也意味着在需要Action<T>的情況下,我將無法在不先進行MySubType情況下調用MySubType方法。

規則允許您使用擴展方法但不使用常規實例方法來執行此操作有多么有趣。

你的“typeof(this)”約束確實應該是“this.GetType()”。 “typeof(this)”沒有任何意義; typeof采用類型,而不是任意表達式。

一旦你意識到,那么我們不能做這種約束的原因就變得更加明確了。 編譯器總是檢查約束 ,但是直到運行時才能確定“this.GetType()”。 這意味着如果我們有這個功能,那么我們會在運行時在類型系統中引入一個故障點:

abstract class Animal
{
    public void Mate<T>(T t) where T : this { ... CENSORED ... }
}
...
Animal x1 = new Giraffe(); 
Mammal x2 = new Tiger();
x1.Mate<Mammal>(x2); 

你不能讓老虎與長頸鹿交配,但程序中的哪個位置編譯器可以檢測到它? 無處。 直到運行時才知道x1和x2的運行時類型,因此在此之前無法檢測到約束違規。

我們討厭這個。 即使在編譯器經過徹底檢查之后,如果某個程序在任何地方都沒有強制類型系統違規而失敗,那么它真的很糟糕。 數組協方差就是這種情況,因為我們支持數組協方差,我們不僅有時會通過編譯器傳遞一個損壞的程序然后崩潰,我們必須減慢每次寫入每個引用類型數組的速度,以便仔細檢查我們沒有違反類型系統。 這很糟糕,我們不希望在類型系統中添加更多的運行時故障點。

這就是為什么我們在C#4中仔細設計新的方差特征,以確保它們始終是類型安全的。 (除非數組上的現有變體轉換不是類型安全的並且將繼續不是類型安全的。)我們希望確保編譯器可以在編譯時檢查所有違規約束,而不是必須吐出執行運行時的新代碼檢查可能意外失敗。

我想你可以通過在最后指定類型來做到這一點。

public void DoStuff<T>(T arg1) where T: YourType

我目前正在解決這個問題,但是YourType是一個界面。 我認為你可以用一個具體的課程來做。

只需使用基類名稱作為約束

public void DoStuff<T>( T arg1 ) where T : BaseClass

在您澄清之后,我認為實現此目的的唯一方法是使您的基類通用。 這有點笨拙,但應該做你需要的。

MySubType foo = new MySubType();
MySubType bar = new MySubType();
foo.DoStuff(bar);

// ...

public class MySubType : MyBaseType<MySubType>
{
}

public class MyBaseType<T>
{
    public void DoStuff(T arg1)
    {
        // do stuff
    }
}

把它改成這個:

public void DoStuff<T>(T arg1) where T : BaseType

這樣,您只能使用從BaseType繼承的類型T.

暫無
暫無

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

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