2014-01-07 36 views
2

舉例來說,我有兩個類執行相同的功能:如何定義一個返回派生類類型實例的泛型方法?

class HDD : Disk 
{ 
    public HDD RMA() 
    { 
     HDD newHDD = RMAService.Exchange(this); 
     return newHDD; 
    } 
} 
class SSD : Disk 
{ 
    public SSD RMA() 
    { 
     SSD newSSD = RMAService.Exchange(this); 
     return newSSD; 
    } 
} 

我想什麼是實現基類中的RMA功能,但保持強類型的返回值,以便用戶可以通過函數簽名確定它們正在獲取它們發送的磁盤類型!


我迄今爲止嘗試:

(見下面的解決方案)

這種類型的定義是醜陋的,但。任何人創建自己的磁盤類或對磁盤的引用都很難知道如何正確使用該類型。

也沒有辦法將類型參數約束爲正在定義的類。看起來很奇怪,沒有一種在編譯時類型是派生類型的基類中聲明屬性或方法的專門方法。

+1

如果我只能在我的日常工作中使用「我很難知道如何使用類型」這個藉口:( –

+0

這可以通過提供'交換'功能來改進。它應該被拆分 - 因爲類型的無知部分留在基類中,並且嚴格類型的部分被子類化,或者可能引入類似於'克隆'的東西。很難說不知道'Exchange'裏面有什麼 – BartoszKP

+0

@ BartoszKP'Exchange'是任何函數,它返回一個與傳入的類型相同的運行時類型的新對象。如果您想要模擬某個東西,只需編寫一個函數,該函數返回傳入的對象。Public T Exchange ( T in){return in;}' – Alain

回答

1

我只是想出了使用擴展方法,看起來好多了這個解決方案。任何理由爲什麼這不工作在編譯時似乎工作?

public abstract class Disk 
{ 
    public Disk() { } 
} 
public static class Disk_Extension_Methods 
{ 
    public static T RMA<T>(this T oldDisk) where T : Disk 
    { 
     T newDisk = RMAService.Exchange(oldDisk) 
     return newDisk; 
    } 
} 

,它允許你去:

public class HDD : Disk { } 
public class SSD : Disk { } 

HDD newHDD = someHDD.RMA(); 
+0

這不會編譯:) – BartoszKP

+0

@BartoszKP - 忘記「靜態」修飾符時,自由手動輸入,但它適用於我。 – Alain

+0

確實,看起來不錯:) – BartoszKP

1

發佈這個答案來設置一個基線,但正如我所說的,我不喜歡這個奇怪的「有一個類將其自身作爲其基類的類型參數」構造。

abstract class Disk<T> where T : Disk<T> //Ugly recursive type parameter 
{ 
    public Disk() 
    { 
     if(this.GetType() != typeof(T)) //Ugly type check 
      throw new Exception("The type argument must be precisely the " + 
           "derived class you're defining. Annoying eh?") 
    } 

    public T RMA() 
    { 
     Disk<T> newDisk = RMAService.Exchange(this) 
     return (T)newDisk; //Ugly explicit cast 
    } 
} 

,它允許你去:

class HDD : Disk<HDD> { } //Ugly self-referencing type parameter 
class SSD : Disk<SSD> { } 

HDD newHDD = someHDD.RMA(); 
+0

我更喜歡這種解決方案,因爲它將與對象相關的邏輯與對象保持相同的類。雖然我喜歡擴展方法,但在這種情況下,他們似乎增加了另一個地方來尋找方法。 – Magus

+0

@Magus - 這真是Microsoft的錯,不允許在非靜態或通用甚至嵌套類中定義靜態擴展方法。從API(和Intellisense)的角度來看,不可能將這種方法與直接在類中定義的方法區分開來。 – Alain

0

不知道這是否是你想要的,但嘗試從派生的子類的公有接口方法分離基類(隱藏)執行。

abstract class Disk //base class 
{ 
    protected Disk CreateRMA() 
    { 
     Disk newDisk; 
     newDisk = new RMAService.Exchange(this); 
     ...whatever else needs to be done... 
     return newDisk; 
    } 
} 

class HDD: Disk 
{ 
    public HDD RMA() 
    { 
     return CreateRMA(); 
    } 
} 

class SSD: Disk 
{ 
    public SSD RMA() 
    { 
     return CreateRMA(); 
    } 
} 

每個派生類的RMA()方法僅調用基類的CreateRMA()方法,然後注塑所得Disk對象派生類型。每個返回的值都有適當的子類型,並且每個RMA()方法的所有實際工作都在基類方法中完成。現在

你能做到這些:

HDD disk1 = someHDD.RMA(); 
SSD disk2 = someSSD.RMA(); 

附錄

這不能解決OP的問題,因爲調用RMAService.Exchange()應具體到(重載)每個派生亞型。一種不同的方法保持在派生類型方法調用,但允許基類做的所有一切的是需要初始化工作,其餘的:

// Second approach 
abstract class Disk //base class 
{ 
    protected Disk CreateRMA(Disk newDisk) 
    { 
     ...other initialization code... 
     return newDisk; 
    } 

    public abstract Disk RMA(); //not sure if this works 
} 

class HDD: Disk 
{ 
    public override HDD RMA() 
    { 
     return CreateRMA(new RMAService.Exchange(this)); 
    } 
} 

class SSD: Disk 
{ 
    public override SSD RMA() 
    { 
     return CreateRMA(new RMAService.Exchange(this)); 
    } 
} 

這對派生類多一點的工作,但只用於超載的new調用;所有其餘的初始化代碼都保存在基類方法中。

投降

好吧,這將不能工作,因爲C#不支持covariant return types

+1

恐怕'Exchange(this)'必須解決編譯時,所以它會尋找'交換(磁盤) ',而不是'交換(HDD)'或'交換(SSD)'。 – MarcinJuraszek

+0

我很害怕這個。那麼你可能不得不像其他人所建議的那樣使用泛型。 –

+1

這裏的另一個問題是它不強制派生類型實現'RMA'方法,所以如果有人試圖將子類'Disk',它不一定有RMA方法。一個'磁盤'不會有一個'RMA'方法,或者 – psubsee2003

1

你可以讓你基線解決方案多一點易於閱讀和使用protected構造和dynamicExchange通話避免Disk構造函數中類型檢查:

abstract class Disk<T> where T : Disk<T> { 
    protected Disk() 
    { 

    } 

    public T RMA() 
    { 
     return RMAService.Exchange((dynamic)this); 
    } 
} 

protected構造使得像class HDD : Disk<SSD>類失敗在編譯時和dynamic延遲Exchange方法重載匹配決定,直到運行時,所以你會得到正確的(或錯誤,當不適合真正this類型)。

+0

有趣。我不知道將一個基類型的引用強制轉換爲「(動態)」會導致在運行時創建正確的重載。這將替代你需要去'typeof(RMAService).GetMethod(「Exchange」)。MakeGenericMethod(new Type [] {this.GetType()})。Invoke(null,new object [] {this} );'??? – Alain

+0

是的,很多是的。 – MarcinJuraszek

+0

我的上帝....非常醜陋的代碼需要儘快修復。 – Alain