具有强类型返回类型的抽象方法

问题描述 投票:0回答: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();
    }
}

我正在寻找一种方法来强制覆盖的

GiveBirth
方法的返回类型,以便它始终返回实际的类类型,这样就不会产生
WeirdHuman
了。

我觉得答案是关于泛型类型的,但我不知道如何做到这一点。

预期结果示例:

Monkey

“绝对不可能”
可能

是一个答案,如果解释清楚的话。

c# generics inheritance overriding abstract-class
5个回答
6
投票

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>(); } }

请注意,单独的协变仍然不足以确保不会形成行为不当的派生类型,但它允许将返回的类型指定为正在使用的类型。但仍然没有办法将其从抽象类中锁定。您也许可以通过在基础级别实现的方法的反射来管理运行时检查,该方法将在运行时检查类型,但这也可能非常混乱。


3
投票

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> { }



3
投票
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(); } }

的返回类型与类型参数相同;但这就是它的全部作用。在某些情况下,这已经足够了,但可能不适合您的目的。


不过,也许这种方法值得了解。


0
投票
基类由于各种原因无法通用

,则此方法可能很有用: 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(); } }



0
投票
dotnetfiddle

中,它就可以工作(注意各种子类型上 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()”

© www.soinside.com 2019 - 2024. All rights reserved.