【问题标题】:Preferred style (delegates over abstract Classes or interfaces)首选风格(代表抽象类或接口)
【发布时间】:2011-02-27 09:05:05
【问题描述】:

我正在编写一个实用程序类库。 A 编写了一个足够通用的工厂类,通过从调用者传入的一些委托,它可以在许多不同的情况下重用。但是,我的一位同事建议我使用抽象类或接口,以便更明确需要重写哪些函数。在我为使用该库而创建的模型中,您将需要覆盖 2 个类,而不仅仅是一个。什么标准决定何时适合使用委托函数或接口。

使用委托模式。


Class A 
{
    Func<string , ReultClass> fun1 {get;set;}

    FactoryObj CreateObj()
    {
         return fun1("")
    }
}

使用接口模式

Class B 
{
    InterfaceObj{get;set;}

    FactoryObj CreateObj()
    {
         return InterfaceObj.fun1("")
    }
}

【问题讨论】:

  • 这取决于他们实际做什么。
  • 一般来说,这些函数需要告诉类如何解释调用者特定的数据。通常是一到两行函数,这就是我选择委托模式的原因。

标签: c# architecture


【解决方案1】:

我认为您会想要更改方法的签名。与其将委托设置为您的类的属性,不如将其设置为您需要传入的参数。然后,您甚至无法在不传入所需数据的情况下调用 CreateObj 方法,并且签名本身会告诉您需要传递的内容使方法调用成功。

【讨论】:

    【解决方案2】:

    工厂是否/应该知道各种“具体”版本?是否有固定的、相对较少的具体具体版本?如果是的话,恕我直言,抽象基础是一个很好的方法。如果调用者应该能够传入“任意”代码,您可以传入委托,或者如果多个调用者可能想要处理/解释相同的数据,将其公开为来自基础的事件并让各种调用者连接起来做他们的解释。

    不幸的是,正如 SLaks 所提到的,这在一定程度上取决于具体情况 - 很难做出任何笼统的陈述 - 在某些情况下,即使上述陈述也可能是一个坏主意 :)

    【讨论】:

      【解决方案3】:

      如果设计是这样的类依赖于另一个类(甚至是一个简单的类),我会推荐接口方法,因为对象确实必须依赖于外部代码:

      interface IA
      {
          ResultClass Fun1(string arg);
      }
      

      在这种情况下,您仍然可以使用委托;我通常用显式接口实现来编写我的“匿名”类(微软称它们为),因此委托属性的名称与其实现的接口方法的名称相同:

      class AnonymousA : IA
      { 
          Func<string, ResultClass> Fun1 { get; set; }
          ResultClass IA.Fun1(string arg) { return this.Fun1(arg); }
      } 
      

      如果涉及到泛型(例如,IA&lt;ResultClass&gt; 和对应的AnonymousA&lt;ResultClass&gt;),那么定义一个工厂类是有用的,只是为了清理创建语法:

      static class Anonymous
      {
          public static IA<ResultClass> A<ResultClass>(Func<string, ResultClass> fun1)
          { return new AnonymousA<ResultClass> { Fun1 = fun1 }
      }
      

      【讨论】:

        【解决方案4】:

        使用包装类,可以做任何可以通过使用接口的委托完成的事情,反之亦然。要确定哪个适合特定情况,请检查常见的使用模式。

        例如,假设一个人想要在运行时构造一个在处理某个对象时需要执行的操作列表。最常见的操作是对 IDisposable 类型的对象调用 Dispose,但在某些情况下可能需要执行其他操作(例如取消订阅事件处理程序)。

        一种方法是维护一个 MethodInvoker 委托列表并依次调用每个委托 (*)。如果清理需要在对象上调用 Dispose,请为 thatObject.Dispose() 创建一个新委托并将其添加到列表中。另一种方法是保留一个 IDisposable 对象列表,并通过创建一个名为 InvokeOnDispose 的类来处理其他操作,该类包含一个 MethodInvoker,并在调用 Dispose 时调用它。

        前一种方法需要为每个清理操作创建一个新的委托,无论是简单地涉及对某事调用 Dispose,还是执行其他操作。后一种方法在将 IDisposable 对象添加到列表时不需要创建任何新的对象实例,但添加一些其他操作需要在委托之外创建 InvokeOnDispose 对象。

        (*) 可以简单地使用 MulticastDelegate 而不是委托列表,但在某些情况下(如对象清理),一个清理操作引发的异常可能不会中断其他操作。虽然可以使用 MulticastDelegate 组装清理操作的集合,然后使用 GetInvocationList 在单独的“try-catch”块中运行每个操作,但从一开始就简单地使用委托列表会更容易和更有效。

        【讨论】:

          猜你喜欢
          • 2012-08-07
          • 2013-05-16
          • 1970-01-01
          • 1970-01-01
          • 2012-11-06
          • 1970-01-01
          • 2011-01-09
          • 2011-03-28
          • 2011-04-13
          相关资源
          最近更新 更多