我有一个基本抽象类,它也实现了一个特定的接口。
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) {}
答案 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"