【问题标题】:How to satisfy the compiler when only partially implementing an interface with an abstract class?仅使用抽象类部分实现接口时如何满足编译器?
【发布时间】:2013-08-08 14:34:51
【问题描述】:

我在这里有一个名为IFish 的接口。我想用提供不完整实现的抽象类 (WalkingFishCommon) 派生它,因此从WalkingFishCommon 派生的类不必实现CanWalk 属性:

interface IFish
{
    bool Swim();
    bool CanWalk { get; }
}

abstract class WalkingFishCommon : IFish
{
    bool IFish.CanWalk { get { return true; } }

    // (1) Error: must declare a body, because it is not marked
    // abstract, extern, or partial
    // bool IFish.Swim();

    // (2) Error: the modifier 'abstract' is not valid for this item
    // abstract bool IFish.Swim();

    // (3): If no declaration is provided, compiler says 
    // "WalkingFishCommon does not implement member IFish.Swim()"
    // {no declaration}

    // (4) Error: the modifier 'virtual' is not valid for this item
    // virtual bool IFish.Swim();

    // (5) Compiles, but fails to force derived class to implement Swim()
    bool IFish.Swim() { return true; }
}

我还没有发现如何让编译器满意,同时仍然实现强制从WalkingFishCommon 派生的类实现Swim() 方法的目标。特别令人费解的是 (1) 和 (2) 之间的增量,编译器在抱怨 Swim() 没有标记为抽象之间交替,然后在下一次呼吸中抱怨它不能标记为抽象。有趣的错误!

有什么帮助吗?

【问题讨论】:

  • 您可以在 IFish.Swim 中抛出 NotImplementedException,即使它并不是您真正想要的
  • 你必须实现所有的接口。你可以声明一个空方法而不使用它..
  • 好的,如果这只是语言语义的限制,我可以忍受。也许我误读了似乎表明这是可能的消息来源......
  • public abstract bool Swim(); 可以工作,但您似乎希望显式实现接口...
  • @Stabledog:您正在显式实现接口(没有public 方法,而是IFish. 前缀)。这意味着不能在这些鱼的实例上调用它们除非您首先将该实例转换为IFish。如果您没有显式实现,那么您可以在任何实现该接口的类型上调用该方法。见stackoverflow.com/questions/143405/…

标签: c# abstract-class


【解决方案1】:

只需将Swim 声明为abstract,不要尝试对其使用显式接口声明(即删除IFish)。

abstract class WalkingFishCommon : IFish
{
    public bool CanWalk { get { return true; } }
    public abstract bool Swim();
}

【讨论】:

  • Nitpick:这不完全相同:现在你可以做((WalkingFishCommon)new SomeFish()).Swim()
  • @ThomasLevesque 如果他这样做了,我会重新审视它。我认为他可能不需要显式接口实现,并且显然试图允许它使问题大大复杂化。
  • 事实证明,我没有具体的理由去做“显式”实现。我只是遇到了一个误解——我认为需要在派生方法声明中指定接口名称,或者最终得到一个不相关的同名方法。所以这个答案澄清并解决了问题。谢谢
  • @Stabledog 如果显式实现了接口方法,那么您可以通过接口类型的标识符调用该方法;如果您有明确实现接口的类实例,则必须先将其类型转换为接口,然后才能调用这些方法。根据我的经验,除非需要,否则人们很少使用显式实现。见:msdn.microsoft.com/en-us/library/vstudio/ms173157.aspx
  • @Stabledog 这也很有用,因为如果你有不同的接口,所有的方法都带有CountLengthSize这样的方法,那么你不会让所有三个成员都混淆智能感知;您可以显式实现一个,而隐式实现另外三个,以便提供功能,但不要混淆试图弄清楚 CountLength 是否实际上是同一件事的用户。
【解决方案2】:

通常,通过在类中为接口的每个成员定义一个公共成员来隐式实现接口:

class MyFish : IFish
{
    public bool CanWalk { get { return ...; } }

    public bool Swim() { return ...; }
}

如果您不想为这些成员之一提供实现,您可以简单地将其抽象化:

abstract class FishBase : IFish
{
    public virtual bool CanWalk { get { return true; } }

    public abstract bool Swim();
}

如果你真的需要实现接口explicitly,你可以创建两个成员:一个必须被派生类覆盖的抽象成员,一个实现接口并将调用转发给第一个成员的成员:

abstract class FishBase : IFish
{
    public virtual bool CanWalk { get { return true; } }

    protected abstract bool Swim();

    bool IFish.Swim() { return Swim(); }
}

【讨论】:

    【解决方案3】:

    如果你真的不需要显式实现接口,你可以简单地这样做:

    abstract class WalkingFishCommon : IFish {
        public abstract bool CanWalk { get; }
        public abstract bool Swim();
    
    }
    

    如果显式实现很重要,可以通过引入受保护的抽象方法来解决问题:

    abstract class WalkingFishCommon : IFish {
        bool IFish.CanWalk { get { return CanWalkCore; } }
        bool IFish.Swim() { return SwimCore(); }
    
        protected abstract bool CanWalkCore { get; }
        protected abstract bool SwimCore();
    
    }
    

    【讨论】:

    • protected 方法不会隐式实现接口,只有public 方法会。
    • 顶层代码不满足IFish接口无法编译
    • @JeanHominal,这是怎么回事?受保护的方法不是接口的实现,它们甚至与接口中的名称不同......
    • 我说的是你的第一个样本,而不是第二个。
    • @Stabledog,是的,完全正确。您通过为接口成员提供实现来满足编译器的要求,并且您仍然强制派生类实现抽象方法。
    【解决方案4】:

    不完全是一个解决方案,但也许你可以做类似的事情

    interface IWalkingFish
    {
        bool CanWalk { get; }
    }
    
    interface ISwimmingFish
    {
        bool Swim();
    }
    
    interface IFish : ISwimmingFish, IWalkingFish
    { }
    
    abstract class WalkingFishCommon : IWalkingFish
    {
        bool IWalkingFish.CanWalk { get { return true; } }
    }
    

    然后您可以为抽象类和具体类使用不同的接口。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-16
      • 2010-12-14
      • 1970-01-01
      • 1970-01-01
      • 2012-07-17
      • 2020-10-20
      • 2012-02-28
      相关资源
      最近更新 更多