考虑以下课程:
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();
}
}
我正在寻找一种方法来强制覆盖的
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();
}
}
或者,如果您不需要任何进一步的继承,则可以关闭泛型。
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();
}
}
可能是可以接受的,因此可以排除像这样的类
public class Animal<T> where T : Animal<T> { }
你真正想要的是像 Haskell 的类型类,你可以在其中抽象类本身的具体类型。在 C# 中最接近的方法是定义一个实现所需功能的代理对象,然后将其传递到您需要的任何地方。
就您而言,这意味着创建一个用于分娩的界面,并为每种具体动物类型实现它。
需要此功能的方法需要“类型类实例”的额外参数。这些方法可以限制通用动物类型相同:
public class WierdHuman<Monkey> { }
public interface ISpawn<T> where T : Animal
{
public T GiveBirth();
}
public void Populate<T>(T parent, ISpawn<T> spawn) where T : Animal
{
}
的实现者实现一个
Animal<T>
方法,该方法返回与类型参数相同的类型,而类型参数本身被限制为一种动物。这不完全是你想要的,但只是为了让你能看到:
Animal<T> GiveBirth()
如果您注释掉
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.
}
}
方法,您会看到编译器会抱怨并显示如下内容:
错误1“ConsoleApplication1.Monkey”未实现继承的抽象成员“ConsoleApplication1.Animal.GiveBirth()”
不幸的是,您必须使用
public override Animal<Monkey> GiveBirth()
语法声明类,但这也许对您有用。
(另请参阅此线程。
SomeKindOfAnimal: Animal<SomeKindOfAnimal>
也就是说,它约束类型参数是一种动物,同时也约束
public class Monkey: Animal<WeirdHuman>
{
public override Animal<WeirdHuman> GiveBirth()
{
return new WeirdHuman();
}
}
的返回类型与类型参数相同;但这就是它的全部作用。在某些情况下,这已经足够了,但可能不适合您的目的。
不过,也许这种方法值得了解。
,则此方法可能很有用:
GiveBirth()
如果您无法向子类添加接口
,并且仍然无法向基本类型添加泛型,此方法可能很有用:
(不幸的是,您不能使 GiveBirthImpl 受到保护,因为辅助类不允许位于基类内部)
abstract class Animal {
}
interface ICanGiveBirth<T> {
T GiveBirth();
}
static class CanGiveBirthHelper {
public static T GiveBirth<T>(this T v) where T: ICanGiveBirth<T> => v.GiveBirth();
}
class Monkey : Animal, ICanGiveBirth<Monkey> {
public Monkey GiveBirth() {
throw new NotImplementedException();
}
}
class Snake : Animal, ICanGiveBirth<Snake> {
public Snake GiveBirth() {
throw new NotImplementedException();
}
}
在这两种情况下,这都会按预期工作:
abstract class Animal {
public abstract T GiveBirthImpl<T>() where T:Animal;
}
static class CanGiveBirthHelper {
public static T GiveBirth<T>(this T v) where T: Animal => v.GiveBirthImpl<T>();
}
class Monkey : Animal {
public override T GiveBirthImpl<T>() {
throw new NotImplementedException();
}
}
class Snake : Animal {
public override T GiveBirthImpl<T>() {
throw new NotImplementedException();
}
}
中,它就可以工作(注意各种子类型上 GiveBirth 的返回类型):
class Tester
{
Monkey TestIt() => new Monkey().GiveBirth();
}
如果我尝试使用 .NET Core 3.1,它不会编译并给出以下错误消息
编译错误(第 26 行,第 27 栏):“Snake.GiveBirth()”:目标运行时不支持重写中的协变返回类型。返回类型必须是“Animal”才能匹配重写的成员“Animal.GiveBirth()”
编译错误(第 35 行,第 28 栏):“WeirdHuman.GiveBirth()”:目标运行时不支持重写中的协变返回类型。返回类型必须是“Animal”才能匹配重写的成员“Animal.GiveBirth()”
编译错误(第 18 行,第 28 栏):“Monkey.GiveBirth()”:目标运行时不支持重写中的协变返回类型。返回类型必须是“Animal”才能匹配重写的成员“Animal.GiveBirth()”