【问题标题】:How can I force a class to implements constructor?如何强制类实现构造函数?
【发布时间】:2017-10-28 08:52:10
【问题描述】:

我有一个接口IPopUp

public interface IPopUp 
{
    bool IsCancelable {get;}
    void Show();
    void Close();
    Action MainAction{ get; }
    Action CancelAction{ get; }
}

我有几个实现:IfoPopUpErrorPopUpLoadingPopUp

我正在使用PopUpManager 实例创建PopUps 的实例

   public class PopUpManager
   {
      public void ShowPopUp<T>(string texto) where T:IPopUp 
      {
        ShowPopup (()=>(IPopUp)Activator.CreateInstance (typeof(T)));
      }

      public void ShowPopUp<T>(string texto,Action mainAction)where T:IPopUp 
      {
        ShowPopup (()=>(IPopUp)Activator.CreateInstance (typeof(T), mainAction));
      }

      public void ShowPopUp<T>(string texto,Action mainAction,Action cancelAction)where T:IPopUp 
     {
        ShowPopup (()=>(IPopUp)Activator.CreateInstance (typeof(T), mainAction, cancelAction));
     }

     private void ShowPopup(Func<IPopUp> factoryFunc)
     {
        if (PopUpShowed != null) {
            PopUpShowed.Close ();
        }
        IPopUp popUp = factoryFunc ();
        popUp.Show ();
     } 
}

我不知道如何强制IPopUp 实现来实现我在PopUpManager 中使用的3 种构造函数。 抽象类将不起作用,因为它可以强制构造函数...... 有什么想法吗?

【问题讨论】:

  • 为什么要强制类有构造函数?如果不这样做会出现什么问题?
  • 不幸的是,最好的答案是不要使用界面。不清楚为什么在这里使用一个而不是抽象基类。

标签: c# interface class-design abstract-class


【解决方案1】:

您已经为您的问题找到了一个很好的解决方案。这里有一些关于强制类实现某些构造函数的额外见解。

可以通过接口来完成吗?

C# language specification 5.0 在第 13 章中定义了接口是什么:

一个接口定义了一个契约。实现一个类或结构 接口必须遵守它的契约。接口可以继承自 多个基接口,一个类或结构可以实现多个 接口。
接口可以包含方法、属性、事件和 索引器。接口本身不提供实现 它定义的成员。该接口仅指定 必须由实现 界面。

实例构造函数不是方法,不能成为接口的一部分。这在 13.2 中明确提醒:

接口不能包含常量、字段、运算符、实例 构造函数、析构函数或类型,接口也不能包含 任何类型的静态成员。

有很多理由证明这种语言设计选择是合理的,您可以找到一些关于 SO 的额外解释。另请注意,其他语言也有类似的限制。例如,Java 声明“接口可能无法直接实例化。

可以用抽象类来完成吗?

C# 语言规范在第 10.1.1.1 节中有说明。那:

abstract修饰符用于表示一个类不完整 并且它旨在仅用作基类。 (...) 抽象类不能直接实例化,在抽象类上使用 new 操作符是编译时错误。

长话短说,如果您要在一个抽象类中定义三个不同的构造函数,派生类将不得不调用其中一个构造函数,但不对它们自己的构造函数施加任何约束。请注意,此逻辑也适用于 C++ 或 Java。

抽象类与其派生类的链接就像Shape与实现ShapeSquareRectangleCircle之间的链接:矩形的构造参数可以是与 Circle 的构造参数非常不同,因此Shape 不能对其子项的构造函数施加约束。

可以组合吗?

您希望 PopupManager 实例化具有某些属性的 IPopUp。不幸的是,您不能以安全的方式直接实例化 IPopUpActivator.CreateInstance() 可能会触发异常)。

为了安全起见,您可以强制 IPopUp 要求 MainActionCancelAction 的设置器,并添加构造函数约束(例如 where T:IPopUp, new())以确保可以在没有实例的情况下创建实例惊喜,并且改变了对象以适应需要。

但在某些情况下需要在构造时提供参数。在这种情况下,您可以考虑使用builder pattern 或您选择的工厂方法。请注意,在允许使用静态成员接口的语言中,您甚至可以在 IPopUp 接口中强制设置三个静态工厂方法。

【讨论】:

    【解决方案2】:

    我最终重构了PopUpManager 类,因此它需要一个工厂委托来生成PopUp。 很清楚,实例化PopUps的责任在于调用者有很大的意义。

    public void ShowPopUp(Func<IPopUp> factoryFuncPopUp)
    {
        if (PopUpShowed != null) {
            PopUpShowed.Close ();
        }
        IPopUp popUp = factoryFuncPopUp();
        popUp.Show ();
    }
    

    无论如何,我很想知道哪种方法最适合强制类实现某些构造函数。

    【讨论】:

    • C# 的简单答案是该语言不提供任何方法来声明构造函数参数的类型约束(只有无参数构造函数可以使用 where T : new() 指定为约束)。您使用的解决方案与我在上面的 StackOverflow 上链接的问题的最佳答案基本相同。
    【解决方案3】:

    我遇到了类似的问题。在我的例子中,具有特定属性 [SpecialAttribute] 的类需要有一个无参数的构造函数。

    我添加了一个测试,以便在每次构建后在我们的 CI 服务器上运行,其中搜索具有该属性的类,然后调用 Activator.CreateInstance。如果由于MissingMethodExceptions 导致失败,该测试“失败”并显示缺少无参数构造函数的类。

    这并不完美,因为代码仍然可以编译。但是,带有明确信息的失败自动化测试是向前迈出的重要一步。

    【讨论】:

      猜你喜欢
      • 2021-05-12
      • 2011-09-27
      • 2012-06-22
      • 1970-01-01
      • 1970-01-01
      • 2013-01-30
      • 2021-05-24
      • 2012-08-24
      • 2011-02-10
      相关资源
      最近更新 更多