C#接口和基类

11
我有一个C#接口和一个实现该接口的具体类。现在我想创建另一个实现该接口的类。这很简单。
然而,这些类中大多数方法都完全相同,只有少数几个方法会发生变化。
我不想在我的第二个类中复制包含在第一个类中的所有逻辑。
我如何创建第二个类,并使用第一个类中的逻辑,除了额外的东西?
我的接口称为IEventRepository,我的第一个类称为BaseEvents。我现在想要创建一个名为FooBarEvents的新类。
FooBarEvents的类定义是:
public class FooBarEvents : BaseEvents, IEventRepository
我的意图是在每个重复代码的方法中使用 `return base.Method()`。 我假设这不正确?

你为什么要使用接口? - Caspar Kleijne
8个回答

19

FooBarEvents 只需要继承 BaseEvents,而不必实现 IEventRepository 接口, 因为 BaseEvents 已经实现了该接口。如果需要更改一些在 FooBarEvents 中的 IEventRepository 方法的行为,只需覆盖这些方法即可。

编辑:一些示例

interface IEventRepository
{
   void CommonMethodA();
   void CommonMethodB();
   void ImplentationSpecificMethod();
}

abstract class BaseEvents : IEventRepository
{
   public void CommonMethodA()
   { ... }

   public virtual void CommonMethodB()
   { ... }

   public abstract void ImplementationSpecificMethod();

   public void BaseEventsMethod()
   { ... }

   public void BaseEventsMethod2()
   { ... }
}

class FooBarEvents : BaseEvents
{
   public override void CommonMethodB()
   { 
      // now FooBarEvents has a different implementation of this method than BaseEvents
   }

   public override void ImplementationSpecificMethod()
   { 
      // this must be implemented
   }

   public new void BaseEventsMethod2()
   { 
      // this hides the implementation that BaseEvents uses
   }

   public void FooBarEventsMethod()
   { 
      // no overriding necessary
   }
}

// all valid calls, assuming myFooBarEvents is instantiated correctly
myFooBarEvents.CommonMethodA()
myFooBarEvents.CommonMethodB()
myFooBarEvents.BaseEventsMethod();
myFooBarEvents.BaseEventsMethod2();
myFooBarEvents.FooBarEventsMethod();
myFooBarEvents.ImplementationSpecificMethod();

// use the contract thusly:
void DoSomethingWithAnEventRepository(BaseEvents events)
{ ... }

我理解这一点,但FooBarEvents类将是客户端特定的,并且可能有另外10个方法未在BaseEvents中实现(它们是特定的)。让BaseEvents拥有10个虚拟方法并且什么也不做,只抛出未实现异常或无效操作异常,这不是一个好的做法吗? - Paul
我不确定我理解你的问题。你是说你会向 FooBarEvents 添加10个客户端特定的方法,但为了覆盖它们,你必须将这些事件的签名添加到 BaseEvents 中吗?那并不是这样的;你可以直接将实现添加到 FooBarEvents 中。我会编辑我的回答并添加一个例子。 - Esoteric Screen Name
如果我不将这些签名添加到BaseEvents中,那么我会收到一个错误,指出BaseEvents没有实现某些接口成员。我正在使用依赖注入,因此我需要将合同添加到接口中,以获取我想要在FooBarEvents类中的成员。 - Paul
啊,我明白了。是的,那么BaseEvents需要为接口中的每个方法添加一个签名行。在这种情况下,您应该将BaseEvents声明为abstract,将这些缺失的签名添加到类中(也作为abstract),实现公共方法(来自接口),并使用BaseEvents作为您的契约类型,而不是IEventRepository。这样,您可以确保所有契约消费者都可以访问公共实现(如在BaseEvents中编码的那样),但任何需要特定于客户端的内容(即在FooBarEvents中实现的内容)仍然具有具体类的自定义版本。将进行编辑。 - Esoteric Screen Name

6

因为 BaseEvents 已经实现了 IEventRepository,所以在 FooBarEvents 中您不需要再次实现它。 FooBarEvents 会自动继承 BaseEvents 的实现。


5
以下代码展示了如何通过抽象基类提供一些接口方法的常规实现,并为其他方法提供自定义实现。
public interface IEventRepository
{
  void Method1();
  void Method2();
}

public abstract class BaseEvents : IEventRepository
{
  public void Method1() 
  {
    Console.WriteLine("This is shared functionality");
  }

  public abstract void Method2();
}

public class Implementation1 : BaseEvents
{
  override public void Method2()
  {
    Console.WriteLine("Impl1.Method2");
  }
}

public class Implementation2 : BaseEvents
{
  override public void Method2()
  {
    Console.WriteLine("Impl2.Method2");
  }
}

public class Program
{
  static void Main(string[] args)
  {
    var implementations = new List<IEventRepository> { new Implementation1(), new Implementation2() };

    foreach (var i in implementations) 
    {
       Console.WriteLine(i.GetType().Name);
       Console.Write("\t");
       i.Method1();  // writes 'This is shared functionality'

       Console.Write("\t");
       i.Method2(); // writes type specific message
    }
  }

}


在这种情况下,我们可以有一个显式接口实现声明吗?我的意思是像IEventRepository.Method2() {}这样的东西。 - remio
当然可以,但我并不真正理解显式接口的价值。 - Jason
以我的观点来看,它通过帮助客户类忘记底层实现来强制类型。 - remio

3
为什么不在基类中将方法定义为 Virtual,并在子类中 覆盖重写 想要更改的方法呢?

2
你可以让你的第二个类扩展你的第一个类。你的第一个类可以是抽象的,但只实现接口中的通用方法。

“你可以让你的第二个类继承你的第二个类” - 你是指“继承你的第一个类”吗? - Samuel Slade
我正在使用不支持抽象类的 Ninject 依赖注入框架。 - Paul
您将在容器中注册实现类 - 在下面的示例中是Implementation1和Implementation2。抽象类无法实例化,因此它们无法与容器一起使用。 - Jason

2

使用继承:

public interface IFoo
{
    void GeneralBehaviorMethod1();
    void GeneralBehaviorMethod2();
    void SpecificBehaviorMethod1();
}

public class Bar: IFoo
{
     public void GeneralBehaviorMethod1() {...}
     public void GeneralBehaviorMethod2() {...}

     public virtual void SpecificBehaviorMethod1() {...}
     ...
}

public class BarOnSteroids: Bar
{
    public override void SpecificBehaviorMethod1() {...}
}
< p > BarOnSteroids 将继承 Bar 的所有行为,您可以通过在< code> BarOnSteroids 中重写这些方法并将它们标记为基类< code> Bar 中的虚拟方法来更改任何所需方法的特定行为。

这样你就会有以下内容:

IFoo iFoo = new Bar();
iFoo.SpecificBehaviorMethod1(); //Bar implementation will be called;

IFoo iFoo = new BarOnSteroids();
iFoo.SpecificBehaviorMethod1(); //BarOnSteroids implementation will be called.
iFoo.CommonBehaviorMethod1(); //Bar implementation will be called.

Bar bar = new BarOnSteroids();
bar.SpecificBehaviorMethod1(); //BarOnSteroids implementation will be called.
bar.CommonBehaviorMethod1(); //Bar implementation will be called.

假设您想更改属于IFoo接口的方法的特定行为。如果您只想向BarOnSteroids添加附加功能,则只需从Bar继承以继承所有功能,并添加所有必需的新方法来实现新功能。

所以,如果我使用Bar消耗IFoo并调用SpecificBehaviourMethod1,我猜我会得到一个NotImplementedException,因为BarOnSteroids做了一些Bar不会做的事情? - Paul
@Pual。简而言之,不是的,BarBarOnSteroids都必须实现SpecificBehaviorMethod1,因为这个方法是IFoo合同的一部分。由于SpecificBehaviorMethod是虚拟的,它将调用特定于您正在调用的实例的实际类型的实现。 - InBetween

1

有几种不同的方法。

一。完全跳过接口,将其变为抽象类。当它起作用时,这更简单,但只能拥有一个基类的事实限制了 C# 中的使用。

public abstract class EventRepository
{
  public abstract int MustBeOverridden(string str);//classes have to override this
  public virtual int CanBeOverridden(int i)//classes can override but may choose not to.
  {
    return 4;
  }
  public int CannotOverride(string str)//this is always the same
  {
    return MustBeOverridden(str) + 3;//can make use of this
  }
}

你可以让一个类实现接口,另一个类从它派生:

public interface IEventRepository
{
  int Method1(string str);
  int Method2(string str);
}

public class EventClass1 : IEventRepository
{
  public int Method1(string str)//can't be overridden as not marked virtual
  {
    return 1;
  }
  public virtual int Method2(string str)//can be overridden
  {
    return 2;
  }
}

public class EventClass2 : EventClass1
{
  public override int Method2(string str)
  {
    return -2;
  }
}

让它们都覆盖一个抽象类,该抽象类提供一些共同的行为:

public abstract class EventClass : IEventRepository
{
  public abstract int Method1(string str);
  public int Method2(string str)
  {
    return 2;
  }
}

public class EventClass1 : EventClass
{
  public override int Method1(string str)
  {
    return 1;
  }
}
public class EventClass2 : EventClass
{
  public override int Method1(string str)
  {
    return -1;
  }
}

他们可能还会使用一个静态的帮助类,与层次结构无关,但提供的方法有助于实现功能。

但要小心这种模式:

public class EventClass1 : IEventRepository
{
  public int Method1(string str)//not override-able
  {
    return 1;
  }
  public int Method2(string str)//not override-able
  {
    return 2;
  }
}
public class EventClass2 : EventClass1, IEventRepository
{
  //We really want our own Method1!
  public new int Method1(string str)
  {
    return 3;
  }
  int IEventRepository.Method1(string str)
  {
    return -1;
  }
}

EventClass2 e2 = new EventClass2();
EventClass1 e1 = e2;
IEventRepository ie = e2;
Console.WriteLine(e2.Method1(null));//3
Console.WriteLine(e1.Method1(null));//1
Console.WriteLine(ie.Method1(null));//-1

即使IEventRepository.Method1被更明智地定义,上述情况仍可能导致混淆。


0
如果在 IEventRepositoryBaseEvents 实现中的某些方法 总是 保持相同的实现,那么您可以只在 BaseEvents 类中实现它们,并将那些可能会更改的标记为virtual。这样,如果 FooBarEvents 希望更改其中一个方法的实现,它只需覆盖它即可。
关于将 IEventsRepository 添加到您的 FooBarEvents 类中的说明:这是有效的。请参见此处Jon Skeet的答案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接