【问题标题】:How do you force constructor signatures and static methods?你如何强制构造函数签名和静态方法?
【发布时间】:2008-10-02 07:42:04
【问题描述】:

有没有办法强制(子)类在 C# 或 Java 中具有具有特定签名或特定静态方法的构造函数?

您显然不能为此使用接口,而且我知道它的用途有限。我发现它很有用的一个例子是当你想强制执行一些设计准则时,例如:

例外情况
它们都应该有四个规范的构造函数,但是没有办法强制执行。您必须依靠像 FxCop(C# 案例)这样的工具来捕获这些。

运营商
没有约定可以对两个类求和(在 C# 中使用 operator+)

是否有任何设计模式可以解决这个限制? 在未来的 C# 或 Java 版本中,可以向 语言 添加什么结构来克服这个限制?

【问题讨论】:

  • 我认为这不是特定于 C# 的。 IIRC 甚至 Java 也不允许抽象静态方法。

标签: c# java oop language-features language-design


【解决方案1】:

使用泛型,您可以强制类型参数具有无参数构造函数 - 但这就是它的限制。

除了泛型之外,即使存在这些限制,实际使用这些限制也会很棘手,但它有时对类型参数/参数很有用。在接口(或可能是静态接口)中允许静态成员同样有助于解决“通用数字运算符”问题。

wrote about this不久前遇到了类似的问题。

【讨论】:

    【解决方案2】:

    在编译时没有强制执行,但我花了很多时间研究类似的问题; generic-enabled maths library 和高效(非默认)ctor API 均在 MiscUtil 中可用。但是,这些仅在运行时首次使用时检查。实际上这不是什么大问题——你的单元测试应该很快找到任何缺失的操作符/ctor。但它可以工作,而且速度非常快......

    【讨论】:

      【解决方案3】:

      您可以使用工厂模式。

      interface Fruit{}
      
      interface FruitFactory<F extends Fruit>{
         F newFruit(String color,double weight);
      
         Cocktail mixFruits(F f1,F f2);
      }
      

      然后您可以为任何类型的水果创建类

      class Apple implements Fruit{}
      class AppleFactory implements FruitFactory<Apple>{
         public Apple newFruit(String color, double weight){
             // create an instance
         }
         public Cocktail mixFruits(Apple f1,Apple f2){
             // implementation
         }
      }
      

      这并不强制您不能使用工厂以外的其他方式创建实例,但至少您可以指定您将从工厂请求的方法。

      【讨论】:

        【解决方案4】:

        强制构造函数

        你不能。最接近的方法是将默认构造函数设为私有,然后提供一个具有参数的构造函数。但是还是有漏洞的。

        class Base
        {
          private Base() { }
          public Base(int x) {}
        }
        
        class Derived : Base
        {
          //public Derived() { } won't compile because Base() is private
          public Derived(int x) :base(x) {}
          public Derived() : base (0) {} // still works because you are giving a value to base
        }
        

        【讨论】:

          【解决方案5】:

          语言中的问题是静态方法实际上是二等公民(构造函数也是一种静态方法,因为您不需要实例来开始)。

          静态方法只是具有命名空间的全局方法,它们并不真正“属于”它们所定义的类(好吧,它们可以访问类中的私有(静态)方法,仅此而已)。

          编译器层面的问题是,没有类实例就没有虚函数表,这意味着你不能使用所有继承和多态的东西。

          我认为可以通过为每个类添加一个全局/静态虚拟表来使其工作,但如果尚未完成,可能有一个很好的理由。

          【讨论】:

          • 如果你仔细想想,问题是“你将如何访问被覆盖的静态方法?”。覆盖有用的唯一方法是如果您有一个类可能会有所不同的对象。如果你打算通过一个实例来访问它,你也可以把它变成一个实例方法。
          【解决方案6】:

          如果我是语言设计师,我会解决这个问题。

          允许接口包含静态方法、运算符和构造函数。

          interface IFoo  
          {  
            IFoo(int gottaHaveThis);  
            static Bar();  
          }
          
          interface ISummable
          {
                operator+(ISummable a, ISummable b);
          }
          

          不允许对应的new IFoo(someInt)IFoo.Bar()

          允许构造函数被继承(就像静态方法一样)。

          class Foo: IFoo
          {
            Foo(int gottaHaveThis) {};
            static Bar() {};
          }
          
          class SonOfFoo: Foo 
          {
            // SonOfFoo(int gottaHaveThis): base(gottaHaveThis); is implicitly defined
          }
          
          class DaughterOfFoo: Foo
          {
            DaughhterOfFoo (int gottaHaveThis) {};
          }
          

          允许程序员转换为接口并在运行时检查转换是否语义有效,即使类没有明确指定。 p>

          ISummable PassedFirstGrade = (ISummable) 10; 
          

          【讨论】:

          • 你会在 IFoo.Bar() 上调用哪个代码?接口中不能有静态方法是有原因的……
          • 如果你有多个实现ISummable的类怎么办?你用 (ISummable) 10 调用哪个构造函数;
          • @Toni, IFoo.Bar() 会产生编译时异常; (ISummable) 10 不需要构造函数。如果“10”没有实现 ISummable 合约(即没有定义 operator+),它会产生异常。
          【解决方案7】:

          不幸的是,你不能在 C# 中。不过,这是一个打击:

          class Program
          {
              static void Main(string[] args)
              {
                  Console.WriteLine(Foo.Instance.GetHelloWorld());
                  Console.ReadLine();
              }
          }
          
          public class Foo : FooStaticContract<FooFactory>
          {
              public Foo() // Non-static ctor.
              {
              }
          
              internal Foo(bool st) // Overloaded, parameter not used.
              {
              }
          
              public override string GetHelloWorld()
              {
                  return "Hello World";
              }
          }
          
          public class FooFactory : IStaticContractFactory<Foo>
          {
              #region StaticContractFactory<Foo> Members
          
              public Foo CreateInstance()
              {
                  return new Foo(true); // Call static ctor.
              }
          
              #endregion
          }
          
          public interface IStaticContractFactory<T>
          {
              T CreateInstance();
          }
          
          public abstract class StaticContract<T, Factory>
              where Factory : IStaticContractFactory<T>, new() 
              where T : class
          {
              private static Factory _factory = new Factory();
          
              private static T _instance;
              /// <summary>
              /// Gets an instance of this class. 
              /// </summary>
              public static T Instance
              {
                  get
                  {
                      // Scary.
                      if (Interlocked.CompareExchange(ref _instance, null, null) == null)
                      {
                          T instance = _factory.CreateInstance();
                          Interlocked.CompareExchange(ref _instance, instance, null);
                      }
                      return _instance;
                  }
              }
          }
          
          public abstract class FooStaticContract<Factory>
              : StaticContract<Foo, Factory>
              where Factory : IStaticContractFactory<Foo>, new() 
          {
              public abstract string GetHelloWorld();
          }
          

          【讨论】:

            【解决方案8】:

            嗯,我从您的问题的措辞中知道您正在寻找编译时强制执行。除非其他人有一个绝妙的建议/黑客可以让你按照你暗示编译器应该的方式这样做,否则我建议你可以编写一个自定义的 MSbuild 任务来做到这一点。像 PostSharp 这样的 AOP 框架可能会通过搭载它的构建任务模型来帮助您在编译时完成此任务。

            但是代码分析或运行时执行有什么问题?也许这只是偏好,我尊重这一点,但我个人对让 CA/FXCop 检查这些事情没有任何问题......如果你真的想强制你的类的下游实现者拥有构造函数签名,你总是可以添加规则运行 -使用反射检查基类构造函数的时间。

            理查德

            【讨论】:

            • 不仅如此:尝试编写一个泛型类,它作为泛型类型接受任何可以求和的对象......没有“理智”的方式来做到这一点。唯一的方法是使用“添加”方法定义一个接口并要求 - 显然,尽管您必须对整数、双精度等进行装箱...
            • 重新“添加” - 是的,您可以:pobox.com/~skeet/csharp/miscutil/usage/genericoperators.html
            • @Marc:好的,但 Jon 的解决方案基本上是疯狂的(不自然,笨拙)。
            • @Skliwz 实际上这是我的代码,而不是 Jon 的。没有冒犯;p 说实话,它运作良好。与在 4.0 中使用 dynamic 没有太大区别 - 你会说这很疯狂吗?不要误会,我不会过度使用dynamic,但它有一些用途
            【解决方案9】:

            我不确定您要达到的目标,您能详细说明一下吗?对不同类强制使用特定构造函数或静态方法的唯一原因是尝试在运行时动态执行它们,这是否正确?

            构造函数旨在特定于特定类,因为它旨在初始化该类的特定需求。据我了解,您希望在类层次结构或接口中强制执行某些内容的原因是它是与正在执行的流程相关的活动/操作,但在不同情况下可能会有所不同。我相信这是多态性的预期好处,这是使用静态方法无法实现的。

            它还需要知道您要为其调用静态方法的类的特定类型,这将打破接口或抽象类试图实现的行为差异的所有多态隐藏。

            如果构造函数所表示的行为旨在成为这些类的客户端之间合同的一部分,那么我会将其显式添加到接口中。

            如果类的层次结构具有相似的初始化要求,那么我将使用抽象基类,但是应该由继承类决定如何找到该构造函数的参数,这可能包括公开相似或相同的构造函数。

            如果这是为了让您在运行时创建不同的实例,那么我建议在抽象基类上使用静态方法,该方法知道所有具体类的不同需求(您可以为此使用依赖注入) .

            【讨论】:

            • 知道其所有子类的父类严重违反了良好的 OO 编程。
            • 同意。我不想这样做,但是在整个层次结构上强制使用相同的静态方法或构造函数也是如此。我只会真正将该方法用作依赖注入框架的包装器,该框架在运行时加载正确的类,作为工厂的快速实现。
            猜你喜欢
            • 1970-01-01
            • 2019-09-11
            • 2023-03-10
            • 2022-07-07
            • 2012-12-10
            • 1970-01-01
            • 1970-01-01
            • 2013-07-24
            • 1970-01-01
            相关资源
            最近更新 更多