【问题标题】:Implementing few methods of a interface class-C#实现接口类的几个方法-C#
【发布时间】:2010-04-27 13:13:51
【问题描述】:

是否有可能在 C# 中实现一个接口,该接口声明了 10 个方法但只实现了 5 个方法,即只定义了该接口的 5 个方法?实际上我有一个由 3 个类实现的接口,并不是所有类都使用了所有方法,所以如果我可以排除任何方法???

我需要这个。这听起来可能是一个糟糕的设计,但并不希望如此。 问题是我有一组用户控件,它们需要具有公共属性,并且基于此,我只在运行时显示它们。由于它是动态的,因此我需要管理它们,因为我拥有属性。一些属性需要少数类而不是全部。并且随着控件的增加,此属性可能会增加,因此我需要一个控件来完全不需要任何使用。只是虚拟方法。同样,我认为如果有办法在课堂的其他部分避免这些方法,那就太好了。听起来除了拥有抽象类或虚拟函数之外别无他法:-(

【问题讨论】:

  • 听起来你的界面不够细化,你需要应用界面隔离原则。
  • 为什么不让公共属性成为接口的一部分,并提供一个特殊的Dictionary 属性来获取给定控件的所有自定义属性?或者为什么不为每种特殊的控件类型使用单独的附加接口?无论如何,您需要检查控件是否支持该自定义属性。只需使用is ICustomControlKind42as ICustomControlKind42 并进行空检查即可。
  • @iPhone:你能简单介绍一下吗?或者你能展示一些相同的链接或例子吗????

标签: c# interface


【解决方案1】:

你可以把它做成一个抽象类,把你不想实现的方法添加为抽象方法。

换句话说:

public interface IMyInterface
{
    void SomeMethod();
    void SomeOtherMethod();
}

public abstract class MyClass : IMyInterface
{
    // Really implementing this
    public void SomeMethod()
    {
        // ...
    }

    // Derived class must implement this
    public abstract void SomeOtherMethod();
}

如果这些类都需要是具体的,而不是抽象的,那么您必须从方法内部抛出一个NotImplementedException/NotSupportedException。但更好的办法是拆分接口,这样实现类就不必这样做了。

请记住,类可以实现多个接口,因此如果某些类具有部分功能但不是全部,那么您希望拥有更精细的接口:

public interface IFoo
{
    void FooMethod();
}

public interface IBar()
{
    void BarMethod();
}

public class SmallClass : IFoo
{
    public void FooMethod() { ... }
}

public class BigClass : IFoo, IBar
{
    public void FooMethod() { ... }
    public void BarMethod() { ... }
}

这可能是你真正应该拥有的设计。

【讨论】:

  • 我认为这是对错误问题的正确答案。我认为真正的问题是界面设计不佳。如果某些接口的某些方法可能没有被某些类实现,则意味着您需要多个接口。并且类将只实现它们所必需的接口
  • FWIW 我想说 SomeMethod 应该是虚拟的。
  • @Brian 实际上我认为这是ICollection 的糟糕设计。通常你不能改变“是集合只读”标志。所以(我再次认为)在只读集合中使用这样的方法是没有意义的。我个人更喜欢假设IMutableCollection 接口扩展IReadOnlyCollection 的设计,但这不是.Net 世界中的做法。在这方面objective-c Cocoa 世界更接近我的理想。
  • @Aaronaught 当然我们会把它变成虚拟的,我为 OP 和未来的 SO 读者提出了这一点,因为这个问题更多的是与 C# 相关的基本 OOD 问题。在抽象类中拥有非抽象、非虚拟方法会导致未来的设计决策受到严重影响,因为它永远不会被覆盖。
  • @Chris:当然,我听到了,这是一个重要的决定——这并不意味着它是一个错误的决定。在抽象类中使用非虚方法有很多充分的理由。这只是你的不变量是什么的问题。看看MarshalByRefObject.GetLifetimeServiceWebRequest.ImpersonationLevel 的两个例子——这两个抽象类的具体方法都没有意义覆盖。
【解决方案2】:

你打破了接口的使用。您应该为每种常见行为提供一个单独的界面。

【讨论】:

    【解决方案3】:

    这是不可能的。但是您可以做的是为您不想实现的方法抛出NotSupportedExceptionNotImplementedException。或者您可以使用抽象类而不是接口。这样您就可以为您选择不覆盖的方法提供默认实现。

    public interface IMyInterface
    {
      void Foo();
    
      void Bar();
    }
    
    public class MyClass : IMyInterface
    {
      public void Foo()
      {
        Console.WriteLine("Foo");
      }
    
      public void Bar()
      {
        throw new NotSupportedException();
      }
    }
    

    或者……

    public abstract class MyBaseClass
    {
      public virtual void Foo()
      {
        Console.WriteLine("MyBaseClass.Foo");
      }
    
      public virtual void Bar()
      {
        throw new NotImplementedException();
      }
    }
    
    public class MyClass : MyBaseClass
    {
      public override void Foo()
      {
        Console.WriteLine("MyClass.Foo");
      }
    }
    

    【讨论】:

    • 另外,如果您不希望未实现的方法在 Intellisense 上可见,您可以显式实现它们。例如: public class MyClass : IMyInterface { ... public void IMyInterface.MyNotImplementedMethod() { throw new NotImplementedException();这样,如果你有一个 MyClass 类型的变量,显式实现的方法将不会显示在 Intellisense 上。
    • 是的,如果你有对象作为对此类的引用,则显式实现的方法将不可见。如果您将其作为对界面的引用,它们当然是可见的。
    • 如果我使用抽象类概念,那就太好了。谢谢
    • @Anero:很好地调用了显式接口实现思想。
    【解决方案4】:

    虽然我同意@PoweRoy 的观点,但您可能需要将接口分解成更小的部分,您可能可以使用显式接口为您的接口实现提供更清晰的公共 API。

    例如:

    public interface IPet
    {
       void Scratch();
       void Bark();
       void Meow();
    }
    
    public class Cat : IPet
    {
        public void Scratch()
        {
            Console.WriteLine("Wreck furniture!");
        }
    
        public void Meow()
        {
           Console.WriteLine("Mew mew mew!");
        }
    
        void IPet.Bark()
        {
            throw NotSupportedException("Cats don't bark!");
        }
    }
    
    public class Dog : IPet
    {
        public void Scratch()
        {
            Console.WriteLine("Wreck furniture!");
        }
    
        void IPet.Meow()
        {
           throw new NotSupportedException("Dogs don't meow!");
        }
    
        public void Bark()
        {
            Console.WriteLine("Woof! Woof!");
        }
    }
    

    使用上面定义的类:

    var cat = new Cat();
    cat.Scrach();
    cat.Meow();
    cat.Bark(); // Does not compile
    
    
    var dog = new Dog();
    dog.Scratch();
    dog.Bark();
    dog.Meow(); // Does not compile.
    
    
    IPet pet = new Dog();
    pet.Scratch();
    pet.Bark();
    pet.Meow(); // Compiles but throws a NotSupportedException at runtime.
    
    // Note that the following also compiles but will
    // throw NotSupportedException at runtime.
    ((IPet)cat).Bark();
    ((IPet)dog).Meow();
    

    【讨论】:

    • 这是一个非常恰当的例子,因为牛不能叫,狗不能叫,但我仍然不能隔离界面 hmmm:-(
    【解决方案5】:

    你可以简单地让你不想实现的方法抛出一个“NotImplementedException”。这样你仍然可以像往常一样隐含界面。

    【讨论】:

    • 我不能使用'NotImplementedException',因为异常将被抛出到我不知道这一点的代码中,这违反了我的要求:-(
    • 在这种情况下,我第二个 PoweRoy 的回答
    【解决方案6】:

    不,不是。您必须定义接口的所有方法,但您可以将它们定义为抽象并将实现留给任何派生类。你不能编译一个说实现了接口的类,而实际上它没有。

    【讨论】:

      【解决方案7】:

      这是一个简单的愚蠢示例,说明了我所说的用于不同目的的不同接口。没有通用属性的接口,因为它会使示例复杂化。此外,此代码缺少许多其他好东西(如挂起布局)以使其更清晰。我没有尝试编译这段代码,所以可能有很多错别字,但我希望这个想法很清楚。

      interface IConfigurableVisibilityControl
      {
          //check box that controls whether current control is visible
          CheckBox VisibleCheckBox {get;}
      }
      
      
      class MySuperDuperUserControl : UserControl, IConfigurableVisibilityControl
      {
          private readonly CheckBox _visibleCheckBox = new CheckBox();
      
          public CheckBox VisibleCheckBox 
          {
              get { return _visibleCheckBox; }
          }
          //other important stuff
      }
      
      //somewhere else
      void BuildSomeUi(Form f, ICollection<UserControl> controls)
      {
          //Add "configuration" controls to special panel somewhere on the form
          Panel configurationPanel = new Panel();
          Panel mainPanel = new Panel();
          //do some other lay out stuff
          f.Add(configurationPanel);
          f.Add(mainPanel);
      
          foreach(UserControl c in controls) 
          {
              //check whether control is configurable
              IConfigurableOptionalControl configurableControl = c as IConfigurableVisibilityControl;
              if(null != configurableControl) 
              {
                  CheckBox visibleConfigCB = configurableControl.VisibleCheckBox;
                  //do some other lay out stuff
                  configurationPanel.Add(visibleConfigCB);
              }
              //do some other lay out stuff
              mainPanel.Add(c);
          }
      }
      

      【讨论】:

      • 我会尝试使用这个。谢谢。因此得出的结论是,我需要虚拟方法或具有虚函数的抽象类。我宁愿使用虚拟方法而不是抽象类。感谢您的所有回复。我学到了很多新东西。
      • 不,这样做你不必有虚拟方法。你需要几个不同的界面。您可以为不同的功能添加另一个接口并以相同的方式检查它(它们)。您的每个控件将仅实现控件所需的那些接口。
      【解决方案8】:

      让你的接口在一个抽象类中实现。抽象类将实现 5 个方法并将其余方法保持为虚拟。然后,您的所有 3 个类都应该从抽象类继承。这是您使用 3 个类的客户端代码,无需更改。

      【讨论】:

        【解决方案9】:

        我想动态地将控件添加到我的表单中,因为这是我的要求。我从here 找到了代码。我根据需要对其进行了编辑。所以我有具有公共属性的 IService 类。这是由用户控件实现的。在不同项目的运行时显示。嗯,我有不同的通用界面,这些界面具有项目用于显示控件的属性。很少有控件需要一些额外的方法或人员来实现基于运行时用户选择的上下文菜单。即项目中的值将作为属性传递给控件并显示出来。现在这个菜单只有一个控件,其余的没有这个。所以我想如果有办法不在所有类而不是一个类中使用这些方法。但听起来我需要使用虚拟方法或抽象类。嗯,虚拟方法对我来说比抽象类更可取:-(

        【讨论】:

          【解决方案10】:

          通过实施 SOLID 原则之一,即“接口隔离原则”,其中接口被分解为多个接口。

          【讨论】:

            【解决方案11】:

            除了以上关于设计接口的优秀建议外,如果您确实需要实现某些方法,一个选项是使用“扩展方法”。将需要实现的方法移到接口之外。创建另一个静态类,将这些实现为静态方法,第一个参数为“this interfaceObject”。这类似于 LINQ 中用于 IEnumerable 接口的扩展方法。

            public static class myExtension {
                public static void myMethod( this ImyInterface obj, ... ) { .. }
            ...
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-04-12
              • 2015-07-31
              • 2011-05-16
              • 2020-02-04
              相关资源
              最近更新 更多