【问题标题】:Avoid explicit type casting when overriding inherited methods覆盖继承的方法时避免显式类型转换
【发布时间】:2014-05-28 11:58:15
【问题描述】:

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

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

我的接口方法在移动后返回相同的对象实例,因此我可以使用 chaining 或直接在 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) {}

【问题讨论】:

标签: c# generics inheritance interface-implementation


【解决方案1】:

我建议将接口方法的返回类型更改为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;
}

【讨论】:

  • 我有许多不同的基本抽象类,然后需要扩展方法......这不是我真正想要的......
  • @RobertKoritnik 这意味着您可以编写一次并使用其他解决方案多次重复使用。如果您有许多不同的抽象基类,那么无论如何您提供的任何解决方案都需要复制,除非它们都派生自某个主对象,此时我将退出对象层次结构并重新考虑。
  • @RobertKoritnik - 如果您需要对动物以外的动物执行此操作,您可以使 Move 扩展方法更通用 - 请参阅更新。没有一般方法可以将方法的返回类型限制为接收者的类型,您可以获得的最接近的是循环模板模式。这具有向层次结构中的每个类型添加泛型参数的缺点,并且会很快变得复杂,因此我更喜欢将泛型移到另一个方法之外,如此处所示。然而,这可能会导致泛型类型推断失败。
  • 我现在实施了您的解决方案。我仍然有一些问题,但这将成为一个新问题......
【解决方案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)
    {
    ...
    }
}

【讨论】:

  • @JamesBarrass 同样,您刚刚声明Snake 现在是一个基类,所以可以使用Snake&lt;T&gt; : Animal&lt;T&gt;
  • @PanagiotisKanavos:您在基本抽象类方法实现中返回了null。如果我返回 this 我需要转换它... :(
  • @PanagiotisKanavos 我同意,但我认为重要的是要提到这种模式对于那些可能不知道它的人来说有它的陷阱。
  • @Rik 您指向的文章实际上强调了这种特殊用途,因为它是解决手头任务的地方之一
  • @RuneFS 除了它仍然可以创建一个很奇怪的Snake : Animal&lt;Cat&gt;,而且它仍然不是一个非常漂亮的设计。
【解决方案3】:

怎么样:

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

【讨论】:

    【解决方案4】:

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

    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&lt;TConcrete&gt; 的泛型变量时,您可以自己回答(实际上,这会阻止您拥有该基类的变量)。

    我要做的是明确此要求(使用类或扩展方法 - 在本例中使用另一个名称):

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

    【讨论】:

      【解决方案5】:

      这很混乱,但是通过引入非泛型基接口,扩展方法可以给出想要的结果。如果您不关心将“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"
      

      【讨论】:

        猜你喜欢
        • 2012-11-20
        • 1970-01-01
        • 2012-09-27
        • 1970-01-01
        • 1970-01-01
        • 2014-01-15
        • 2016-01-24
        • 2019-02-08
        • 1970-01-01
        相关资源
        最近更新 更多