2013-04-23 180 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 
{ 
}