如何强制构造函数签名和静态方法

本文关键字:静态方法 何强制 构造函数 | 更新日期: 2023-09-27 17:47:23

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

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

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

运营商
没有协定指定可以对两个类求和(在 C# 中使用运算符 +)

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

如何强制构造函数签名和静态方法

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

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

不久前,当我遇到类似的问题时,我写过这个。

在编译时没有强制执行,但我花了很多时间研究类似的问题;一个支持泛型的数学库和一个高效的(非默认的)ctor API 都可以在 MiscUtil 中使用。但是,这些仅在运行时首次使用时检查。实际上,这不是一个大问题 - 您的单元测试应该很快找到任何缺少的运算符/ctor。但它有效,而且很快...

您可以使用工厂模式。

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
   }
}

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

强制构造函数

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

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
}

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

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

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

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

如果我是语言设计师,我会解决它。

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

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) {};
}

允许程序员强制转换为接口,并在必要时在运行时检查强制转换是否在语义上有效,即使类未显式指定也是如此。

ISummable PassedFirstGrade = (ISummable) 10; 

不幸的是,你不能在 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();
}

好吧,我从您问题的措辞中知道您正在寻找编译时强制。除非其他人有一个绝妙的建议/技巧,允许你按照你暗示编译器应该的方式做到这一点,否则我建议你可以编写一个自定义的 MSbuild 任务来做到这一点。 像PostSharp这样的AOP框架可能会通过捎带它的构建任务模型来帮助你在关键时间完成这项工作。

但是代码分析或运行时强制有什么问题呢? 也许这只是偏好,我尊重这一点,但我个人对让 CA/FXCop 检查这些东西没有任何问题......如果您确实想强制类的下游实现者具有构造函数签名,则始终可以使用反射在基类构造函数中添加规则运行时检查。

理查

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

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

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

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

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

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