2013-04-23 210 views
5

考慮下面的類中使用抽象方法:與強類型的返回類型

public abstract class Animal 
{ 
    public abstract Animal GiveBirth(); 
} 

public class Monkey : Animal 
{ 
    public override Animal GiveBirth() 
    { 
     return new Monkey(); 
    } 
} 

public class Snake : Animal 
{ 
    public override Animal GiveBirth() 
    { 
     return new Snake(); 
    } 
} 

//That one doesnt makes sense. 
public class WeirdHuman: Animal 
{ 
    public override Animal GiveBirth() 
    { 
     return new Monkey(); 
    } 
} 

我在尋找一種方式來執行的返回類型的overrided GiveBirth方法,使其始終返回實際類的類型,從而使沒有WeirdHuman可以生出Monkey

我覺得答案是關於泛型類型,但我看不出我該怎麼做。

爲例預期的結果:

public abstract class Animal 
{ 
    public abstract /*here a way to specify concrete type*/ GiveBirth(); 
} 

public class Monkey : Animal 
{ 
    public override Monkey GiveBirth() //Must returns an actual Monkey 
    { 
     return new Monkey(); 
    } 
} 

「絕對不可能」 可以一個答案,如果解釋清楚。

回答

5

這是co-variant返回並且不受C#支持。我每天都在爲此感嘆。你可以希望做的最好的解決方法是使用泛型返回類型並在泛型類型上指定where條件,但是這也可能導致您在匹配通用參數要求的同時遇到其他問題。

public abstract class Animal<TBirthType> where TBirthType : Animal<TBirthType> 
{ 
    public abstract TBirthType GiveBirth(); 
} 

public class Monkey<TBirthType> : Animal<TBirthType> where TBirthType : Monkey<TBirthType> 
{ 
    public override TBirthType GiveBirth() 
    { 
     return new Monkey<Monkey>(); 
    } 
} 

或者,如果你不需要任何進一步的繼承,你可以關閉泛型。

public class Monkey : Animal<Monkey> 
{ 
    public override Monkey GiveBirth() 
    { 
     return new Monkey(); 
    } 
} 

注意,單獨協方差仍不足以確保沒有行爲不當的派生類型可以形成,但它將使返回類型被指定爲正在使用的類型。儘管如此,仍然沒有辦法從抽象類中鎖定它。你也許可以通過基礎級實現的方法來管理運行時檢查,該方法會在運行時檢查類型,但這也可能非常混亂。

+0

這就是我的想法。我看不到任何方式來指定泛型類型必須實現實際的類類型。就像'在哪T:這個'或什麼...... – Johnny5 2013-04-23 14:40:39

+0

支持它的語言是什麼? – Johnny5 2013-04-23 14:41:19

+0

Java支持它。由於CLR是潛在的問題,.NET沒有任何支持。希望未來的版本能夠增加支持,但據我瞭解,它將需要大幅度的改變才能實現。 – 2013-04-23 14:44:13

1

您可以這樣做,這迫使Animal<T>的實施者實施Animal<T> GiveBirth()方法,該方法返回與type參數相同的類型,該類型參數本身被限制爲一種動物。

那不是很你想要什麼,但只是讓你可以看到:

public abstract class Animal<T> where T: Animal<T> 
{ 
    public abstract Animal<T> GiveBirth(); 
} 

public class Monkey: Animal<Monkey> 
{ 
    public override Animal<Monkey> GiveBirth() 
    { 
     return new Monkey(); 
    } 
} 

public class Snake: Animal<Snake> 
{ 
    public override Animal<Snake> GiveBirth() 
    { 
     return new Snake(); 
    } 
} 

public class WeirdHuman: Animal<WeirdHuman> 
{ 
    public override Animal<WeirdHuman> GiveBirth() 
    { 
     return new Monkey(); // Won't compile of course. 
    } 
} 

如果您註釋掉public override Animal<Monkey> GiveBirth()方法,你會看到,編譯器會抱怨,說是這樣的:

錯誤1 'ConsoleApplication1.Monkey' 不實現繼承的抽象構件 'ConsoleApplication1.Animal.GiveBirth()'

不幸的是,你必須使用SomeKindOfAnimal: Animal<SomeKindOfAnimal>語法聲明這些類,但是這可能適用於你。

Also see this thread.

唉,這完全不是那麼回事,因爲它可以讓你做到這一點:

public class Monkey: Animal<WeirdHuman> 
{ 
    public override Animal<WeirdHuman> GiveBirth() 
    { 
     return new WeirdHuman(); 
    } 
} 

換句話說,它限制了類型參數是一種動物,並且它還將GiveBirth()的返回類型限制爲與類型參數相同;但是這只是它。在某些情況下,這足夠了,但可能不適合你的目的。

不過,也許這種方法值得了解。

+0

然後當有人做出一個'猴子:動物'會發生什麼事情,並且真的讓孩子們變得混亂? – Servy 2013-04-23 14:50:26

+0

@Servy :)那麼,'GiveBirth()'方法仍然只能返回一個'Monkey',所以這就滿足了*的要求,以強制執行重寫的GiveBirth方法的返回類型,以便它總是返回實際班級類型*「(來自OP)。如果你願意,你可以說猴子是一個奇怪的人! – 2013-04-23 14:59:44

+1

但它不限制返回實現接口的類的類型,只是實現接口的類選擇的類型。 – Servy 2013-04-23 15:01:43

1

據我所知,沒有一種純粹的方法來支持這種純粹在單一的類層次結構。使用循環的泛型類型參數,例如如果你控制整個層次結構,因此可以排除類,如

public class WierdHuman<Monkey> { } 

你真正想要的是像Haskell的類型類,

public class Animal<T> where T : Animal<T> { } 

是可以接受的,你可以抽象在具體類型的班級本身。在C#中最接近的是定義一個實現所需功能的代理對象,然後將它傳遞到需要的地方。

就你而言,這意味着創建一個接口來分娩,併爲每個具體的動物類型實現它。

需要此功能的您的方法需要'typeclass實例'的額外參數。這些方法可以將通用動物類型限制爲相同:

public interface ISpawn<T> where T : Animal 
{ 
    public T GiveBirth(); 
} 

public void Populate<T>(T parent, ISpawn<T> spawn) where T : Animal 
{ 
}