11

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

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

5 回答 5

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;
}
于 2014-05-28T12:12:52.000 回答
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)
    {
    ...
    }
}
于 2014-05-28T12:06:39.207 回答
1

怎么样:

public virtual T Move<T>(IMover<int> moverProvider) where T : Animal
{
    // performs movement using provided mover
}
于 2014-05-28T12:08:59.957 回答
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;
    }
}
于 2014-05-28T12:20:40.380 回答
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"
于 2014-05-28T21:54:41.483 回答