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

时间:2013-04-23 14:34:08

标签: c# generics inheritance override abstract-class

考虑以下课程:

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 1}}。

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

预期结果的例子:

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

"绝对不可能"如果清楚解释,可能是一个答案。

4 个答案:

答案 0 :(得分:6)

这是共变量返回,C#不支持。我每天都感叹。你可以希望做的最好的事情是使用泛型返回类型并在泛型类型上指定where条件,但是这也可能导致你在遇到通用参数要求的情况下遇到其他问题。 / p>

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

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

答案 1 :(得分:2)

据我所知,没有一种干净的方法可以纯粹在单个类层次结构中支持它。使用重复的泛型类型参数,例如

public class Animal<T> where T : Animal<T> { }
如果您控制整个层次结构,则可以接受

,因此可以排除类似

的类
public class WierdHuman<Monkey> { }

你真正想要的是像Haskell的类型类,你可以在这里抽象出类本身的具体类型。您可以在C#中获得的最接近的是定义一个实现所需功能的代理对象,然后将其传递到您需要的任何地方。

在您的情况下,这意味着创建一个生育界面,并为每种具体的动物类型实施它。

您需要此功能的方法需要“类型类实例”的额外参数。这些方法可以限制通用动物类型相同:

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

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

答案 2 :(得分: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()的返回类型约束为与类型参数相同;但就是这样。在某些情况下,这已足够,但可能不适合您的目的。

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

答案 3 :(得分:0)

如果您的基类由于各种原因不能通用,此方法可能很有用:

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

如果您无法向子类添加接口,并且仍然无法向基本类型添加泛型,则此方法可能很有用: (不幸的是你不能保护 GiveBirthImpl,因为辅助类不允许在基类中)

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

在这两种情况下,这都会按预期工作:

class Tester
{
   Monkey TestIt() => new Monkey().GiveBirth();
}