25

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

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

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

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

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

4

9 回答 9

11

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

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

不久前,我在遇到类似问题时写了这篇文章。

于 2008-10-02T07:56:52.717 回答
9

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

于 2008-10-02T12:31:08.033 回答
7

您可以使用工厂模式。

interface Fruit{}

interface FruitFactory<F extends Fruit>{
   F newFruit(String color,double weight);

   Cocktail mixFruits(F f1,F f2);
}

然后,您可以为任何类型的 Fruit 创建类

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

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

于 2008-10-02T08:37:26.783 回答
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
}
于 2008-10-02T07:58:19.717 回答
1

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

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

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

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

于 2008-10-02T07:57:04.323 回答
1

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

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

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; 
于 2008-10-02T10:13:46.960 回答
1

不幸的是,你不能在 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();
}
于 2008-10-02T12:15:38.477 回答
0

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

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

理查德

于 2008-10-02T08:02:38.303 回答
0

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

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

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

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

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

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

于 2008-10-02T12:15:51.470 回答