重写继承方法时避免使用显式类型转换

时间:2014-05-28 11:58:15

标签: c# generics inheritance interface-implementation

我有一个基本抽象类,它也实现了一个特定的接口。

public interface IMovable<TEntity, T>
    where TEntity: class
    where T: struct
{
    TEntity Move(IMover<T> moverProvider);
}

public abstract class Animal : IMovable<Animal, int>
{
    ...

    public virtual Animal Move(IMover<int> moverProvider)
    {
        // performs movement using provided mover
    }
}

然后我继承了一些类,其中一些必须覆盖基类的接口实现方法。

public class Snake : Animal
{
    ...

    public override Animal Move(IMover<int> moverProvider)
    {
        // perform different movement
    }
}

我的界面方法在移动后返回相同的对象实例,因此我可以使用链接或直接在return语句中执行某些操作而不使用其他变量。

// I don't want this if methods would be void typed
var s = GetMySnake();
s.Move(provider);
return s;

// I don't want this either if at all possible
return (Snake)GetMySnake().Move(provider);

// I simply want this
return GetMySnake().Move(provider);

问题

正如您在我的示例中所看到的,我在子类中的覆盖返回基类类型而不是运行类。这可能要求我投出结果,这是我想避免的。

如何定义我的界面和实现,以便我的覆盖将返回执行实例的实际类型?

public Snake Move(IMover<int> moverProvider) {}

5 个答案:

答案 0 :(得分:3)

我建议将接口方法的返回类型更改为void,并将链接行为移动到扩展方法,您可以在其中获取目标的真实类型,例如。

public interface IMovable<TEntity, T>
    where TEntity : class
    where T : struct
{
    void MoveTo(IMover<T> moverProvider);
}

public abstract class Animal : IMovable<Animal, int>
{
    public virtual void MoveTo(IMover<int> mover) { }
}

public static class AnimalExtensions
{
    public static TAnimal Move<TAnimal>(this TAnimal animal, IMover<int> mover) where TAnimal : Animal, IMovable<TAnimal, int>
    {
        animal.MoveTo(mover);
        return animal;
    }
}

请注意,如果您需要更广泛地应用Move扩展名,可以使其更通用:

public static TEntity Move<TEntity, T>(this TEntity entity, IMover<T> mover) where TEntity : IMovable<TEntity, T> where T : struct
{
    entity.MoveTo(mover);
    return entity;
}

答案 1 :(得分:2)

您可以将Animal转换为接受具体类型作为类型参数的泛型类型:

public abstract class Animal<T> : IMovable<T, int> where T:Animal<T>        
{


    public virtual T Move(IMover<int> moverProvider)
    {
    ...
    }
}

public class Snake : Animal<Snake>
{


    public override Snake Move(IMover<int> moverProvider)
    {
    ...
    }
}

答案 2 :(得分:1)

怎么样:

public virtual T Move<T>(IMover<int> moverProvider) where T : Animal
{
    // performs movement using provided mover
}

答案 3 :(得分:1)

有时您需要将当前类型作为方法返回值,并且必须在派生类中进行更改。 我避免使用此模式,因为它会导致奇怪的行为和不寻常的语法(如果您的模型变得复杂)但尝试一下(主要因为非常小的层次结构它看起来很简单):

abstract class Animal<TConcrete> : IMovable<TConcrete, int>
where TConcrete : Animal<T>
{
    public virtual T Move(IMover<int> moverProvider) {
        return (T)this; // Cast to Animal<T> to T isn't implicit
    }
}

sealed class Snake : Animal<Snake>
{
    public virtual Snake Move(IMover<int> moverProvider) {
        return this;
    }
}

为什么这么糟糕?当您需要声明Animal<TConcrete>类型的泛型变量时,您可以自己回答(实际上,这会阻止您使用该基类的变量)。

我要做的是明确要求(使用类或扩展方法 - 在这种情况下使用其他名称):

abstract class Animal : IMovable<Animal, int>
{
    // Please note that this implementation is explicit
    Animal IMovable<Animal, int>.Move(IMover<int> moverProvider) {
        return MoveThisAnimal(moverProvider);
    }

    protected virtual Animal MoveThisAnimal(IMover<int> moverProvider) {
        // Peform moving
        return this;
    }
}

class Snake : Animal
{
    public Snake Move(IMover<int> moverProvider) {
        return (Snake)MoveThisAnimal(moverProvider);
    }

    protected override Animal MoveThisAnimal(IMover<int> moverProvider) {
        // Peform custom snake moving
        return this;
    }
}

答案 4 :(得分:0)

这很麻烦,但通过引入非通用基本接口,扩展方法可以提供所需的结果。如果你不关心将'MoveFunc'暴露给调用者,它也可以简化(删除第二个显式接口实现):

public interface IMovable
{
    IMovable MoveFunc();
}

public interface IMovable<TEntity, T> : IMovable
    where TEntity : IMovable
{
    new TEntity MoveFunc();
}

public abstract class Animal : IMovable<Animal, int>
{
    protected virtual Animal MoveFunc()
    {
        // performs movement using provided mover
        Debug.WriteLine("Animal");
    }

    Animal IMovable<Animal, int>.MoveFunc()
    {            
        return MoveFunc();
    }

    IMovable IMovable.MoveFunc()
    {
        return ((IMovable<Animal, int>)this).MoveFunc();
    }
}

public class Snake : Animal
{
    protected override Animal MoveFunc()
    {
         // performs movement using provided mover
         Debug.WriteLine("Snake");
    }
}

public static class IMovableExtensions
{
    public static TOut Move<TOut>(this TOut entity) where TOut : IMovable
    {
        return (TOut)entity.MoveFunc();
    }

}

...

Snake snake = new Snake();

Snake moved = snake.Move(); // "Snake"

Animal animal = snake;

animal.Move() // "Snake"