在重写inheritance方法时避免使用显式类型转换
我有一个基本抽象类,也实现了一个特定的接口。
public interface IMovable where TEntity: class where T: struct { TEntity Move(IMover moverProvider); } public abstract class Animal : IMovable { ... public virtual Animal Move(IMover moverProvider) { // performs movement using provided mover } }
然后我inheritance了一些类,其中一些必须覆盖基类的接口实现方法。
public class Snake : Animal { ... public override Animal Move(IMover 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 moverProvider) {}
我建议将接口方法的返回类型更改为void
并将链接行为移动到扩展方法,您可以在其中获取目标的实际类型,例如
public interface IMovable where TEntity : class where T : struct { void MoveTo(IMover moverProvider); } public abstract class Animal : IMovable { public virtual void MoveTo(IMover mover) { } } public static class AnimalExtensions { public static TAnimal Move(this TAnimal animal, IMover mover) where TAnimal : Animal, IMovable { animal.MoveTo(mover); return animal; } }
请注意,如果您需要更广泛地应用,可以使Move
扩展更通用:
public static TEntity Move(this TEntity entity, IMover mover) where TEntity : IMovable where T : struct { entity.MoveTo(mover); return entity; }
您可以将Animal
转换为接受具体类型作为类型参数的generics类型:
public abstract class Animal : IMovable where T:Animal { public virtual T Move(IMover moverProvider) { ... } } public class Snake : Animal { public override Snake Move(IMover moverProvider) { ... } }
怎么样:
public virtual T Move(IMover moverProvider) where T : Animal { // performs movement using provided mover }
有时您需要将当前类型作为方法返回值,并且必须在派生类中进行更改。 我会避免这种模式,因为它会导致奇怪的行为和不寻常的语法(如果你的模型变得复杂)但是试一试(主要因为非常小的层次结构看起来很简单):
abstract class Animal : IMovable where TConcrete : Animal { public virtual T Move(IMover moverProvider) { return (T)this; // Cast to Animal to T isn't implicit } } sealed class Snake : Animal { public virtual Snake Move(IMover moverProvider) { return this; } }
为什么这么糟糕? 当你需要声明一个Animal
类型的generics变量时,你可以自己回答(实际上这会阻止你拥有一个带有该基类的变量)。
我要做的是明确这个要求 (使用类或扩展方法 – 在这种情况下使用其他名称):
abstract class Animal : IMovable { // Please note that this implementation is explicit Animal IMovable.Move(IMover moverProvider) { return MoveThisAnimal(moverProvider); } protected virtual Animal MoveThisAnimal(IMover moverProvider) { // Peform moving return this; } } class Snake : Animal { public Snake Move(IMover moverProvider) { return (Snake)MoveThisAnimal(moverProvider); } protected override Animal MoveThisAnimal(IMover moverProvider) { // Peform custom snake moving return this; } }
这很麻烦,但通过引入非通用的基本接口,扩展方法可以提供所需的结果。 如果您不关心向调用者公开“MoveFunc”,它也可以简化(删除第二个显式接口实现):
public interface IMovable { IMovable MoveFunc(); } public interface IMovable : IMovable where TEntity : IMovable { new TEntity MoveFunc(); } public abstract class Animal : IMovable { protected virtual Animal MoveFunc() { // performs movement using provided mover Debug.WriteLine("Animal"); } Animal IMovable.MoveFunc() { return MoveFunc(); } IMovable IMovable.MoveFunc() { return ((IMovable)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(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"