ChatGPT解决这个技术问题 Extra ChatGPT

定义构造函数签名的接口?

奇怪的是这是我第一次遇到这个问题,但是:

如何在 C# 接口中定义构造函数?

编辑有些人想要一个例子(这是一个空闲时间项目,所以是的,这是一个游戏)

IDdrawable +更新 +Draw

为了能够更新(检查屏幕边缘等)并绘制自身,它总是需要一个 GraphicsDeviceManager。所以我想确保该对象具有对它的引用。这将属于构造函数。

既然我写下来了,我认为我在这里实现的是 IObservableGraphicsDeviceManager 应该采用 IDrawable...看来要么我没有得到 XNA 框架,要么没有考虑到框架很好。

编辑在接口的上下文中我对构造函数的定义似乎有些混乱。接口确实不能被实例化,所以不需要构造函数。我想要定义的是构造函数的签名。就像接口可以定义某个方法的签名一样,接口也可以定义构造函数的签名。

与其使用定义构造函数的接口,不如使用定义工厂方法的接口。

k
kevinarpe

你不能。偶尔会很痛苦,但无论如何你都无法使用正常的技术来调用它。

在一篇博文中,我建议了 static interfaces,它只能在泛型类型约束中使用 - 但 IMO 真的很方便。

关于如果您可以在接口中定义构造函数的一点,您将无法派生类:

public class Foo : IParameterlessConstructor
{
    public Foo() // As per the interface
    {
    }
}

public class Bar : Foo
{
    // Yikes! We now don't have a parameterless constructor...
    public Bar(int x)
    {
    }
}

我确实可以看到问题,但是您定义的所有其他方法也是如此。通常 NotSupportedException 是唯一的出路。
@boris:不同之处在于,编译器保证总有一些东西可以通过正常继承来调用。在这种情况下,有些东西“应该”存在但不存在。
是的,但这有什么问题,没有合适的 Bar 构造函数,因为它不能正确满足接口。这就像说你不能在接口中定义方法,因为如果你不实现它,它就行不通。
@Gravitas:有用与否,今天肯定不可用。我怀疑如果这个功能出现,它需要比我们在评论中做的更仔细的设计:)
@user1721649:有很多地方我都想要这个——几乎总是有通用约束。基本上,您希望能够在泛型方法中调用构造函数,以创建实现某个接口的泛型类型的实例。这真的很有用。
D
Dan

如前所述,您不能在接口上拥有构造函数。但由于这是大约 7 年后在 Google 中排名很高的结果,我想我会在这里插话——特别是展示如何将抽象基类与现有接口结合使用,并可能减少重构量将来需要类似的情况。一些评论中已经暗示了这个概念,但我认为值得展示如何实际做到这一点。

因此,到目前为止,您的主界面如下所示:

public interface IDrawable
{
    void Update();
    void Draw();
}

现在使用要强制执行的构造函数创建一个抽象类。实际上,自从您编写原始问题以来,它现在就可用了,我们可以在这里稍微花点心思并在这种情况下使用泛型,以便我们可以将其调整到可能需要相同功能但具有不同构造函数要求的其他接口:

public abstract class MustInitialize<T>
{
    public MustInitialize(T parameters)
    {

    }
}

现在您需要创建一个继承自 IDrawable 接口和 MustInitialize 抽象类的新类:

public class Drawable : MustInitialize<GraphicsDeviceManager>, IDrawable
{
    GraphicsDeviceManager _graphicsDeviceManager;

    public Drawable(GraphicsDeviceManager graphicsDeviceManager)
        : base (graphicsDeviceManager)
    {
        _graphicsDeviceManager = graphicsDeviceManager;
    }

    public void Update()
    {
        //use _graphicsDeviceManager here to do whatever
    }

    public void Draw()
    {
        //use _graphicsDeviceManager here to do whatever
    }
}

然后只需创建一个 Drawable 实例就可以了:

IDrawable drawableService = new Drawable(myGraphicsDeviceManager);

这里很酷的是,我们创建的新 Drawable 类的行为仍然与我们对 IDrawable 的期望相同。

如果您需要将多个参数传递给 MustInitialize 构造函数,您可以创建一个类来定义您需要传入的所有字段的属性。


需要强调的是,您不能创建“MustInitialize”类来涵盖所有情况,因为 C# 不允许多重继承。这意味着如果您的类继承一个抽象类也不能继承另一个类。
这是真的@Skarlot,它不是一个灵丹妙药的解决方案。不过,在您指出的问题中,希望直接修改您已经继承的抽象类是有意义的。但是仍然存在不可能和/或不合适的情况,因此需要更深入的设计模式。
在 C# 中,您只能从一个基类继承,因此从 MustInitialize 继承会阻止您从其他类继承。另一种方法是使用一个像构造函数一样的方法 - 设置类属性和字段,可能使用阻止它被成功调用两次的标记,或者在调用该方法之前使用的类。
@andrew pate - 正如Skarllot 所指出的,多重继承确实如此,但在OP 的这种特定情况下,这不是问题。尽管您提出的建议可行,但我不建议编写需要调用公共方法才能正确使用的类。如果您不熟悉该术语,请搜索“时间耦合”。
@Dan,关于时间耦合的好点。 hack 可能是放入一个名为提醒方法的方法,该方法预计不包含实现,例如:ExpectedConstructorSignature_Drawable(GraphicsDeviceManager graphicsDeviceManager)
A
Ajay

一个非常晚的贡献展示了接口构造函数的另一个问题。 (我选择这个问题是因为它最清楚地表达了问题)。假设我们可以:

interface IPerson
{
    IPerson(string name);
}

interface ICustomer
{
    ICustomer(DateTime registrationDate);
}

class Person : IPerson, ICustomer
{
    Person(string name) { }
    Person(DateTime registrationDate) { }
}

按照惯例,“接口构造函数”的实现被类型名称替换。

现在做一个实例:

ICustomer a = new Person("Ernie");

我们会说遵守了合同 ICustomer 吗?

那么这个呢:

interface ICustomer
{
    ICustomer(string address);
}

如果您在 ICustomer 和 IPerson 中有一个签名和名称相同的方法,也会出现此问题。我看不出这有什么帮助。接口不是“仅用于我的定义”。这是为了“不惜一切代价包括我”
@nawfal 关键是接口从不要求执行方法,只要求它应该存在。它永远不能保证状态。相反,“构造函数接口”确实要求在构造对象时完成(执行)某些事情。当有不同的接口时,这永远无法保证。
@GertArnold 方法完成其工作,而构造函数完成其工作。我不知道这里有什么区别。接口签订了“我的实现应该在那里”的合同,而不是“我的应该是唯一的实现”。我会说为了一致性,这应该对构造函数、方法、属性有效
我不明白这是怎么回事,可以简单地制定一个规则,只允许链接具有相同构造函数签名的接口。这与接口 A 实现“foo : int”和接口 B 实现“foo : string”的行为相同,只是它们不兼容。
让我想起了没有虚拟继承的c++多重继承问题
M
Michael

你不能。

接口定义了其他对象实现的契约,因此没有需要初始化的状态。

如果您有一些需要初始化的状态,您应该考虑使用抽象基类。


为什么合同不能有状态?
因为合同约束你提供某种行为。如何使用接口意味着提取常见行为,这不依赖于状态(这将是一个实现细节)。
似乎我们可以使用像“合同”这样的单独机制,而不是接口来要求对象实现某些功能,如构造函数和静态方法/属性,这些功能不会通过合同调用(就像你会通过接口调用一样)。下一个 .Net 版本的功能建议?
J
Jeroen Landheer

我回顾了这个问题,我心想,也许我们以错误的方式解决了这个问题。当涉及定义具有某些参数的构造函数时,接口可能不是要走的路……但是(抽象)基类是。

如果您在那里创建一个带有构造函数的基类,该构造函数接受您需要的参数,那么从它派生的每个类都需要提供它们。

public abstract class Foo
{
  protected Foo(SomeParameter x)
  {
    this.X = x;
  }

  public SomeParameter X { get; private set }
}

public class Bar : Foo // Bar inherits from Foo
{
  public Bar() 
    : base(new SomeParameter("etc...")) // Bar will need to supply the constructor param
  {
  }
}

这也是我解决这个问题的方法。我的接口定义了类需要能够做什么,但我的基本抽象类强制执行它的构造函数组件。
J
Jeroen Landheer

不可能创建定义构造函数的接口,但是可以定义一个强制类型具有无参数构造函数的接口,尽管它是使用泛型的非常丑陋的语法......我实际上不太确定这确实是一个很好的编码模式。

public interface IFoo<T> where T : new()
{
  void SomeMethod();
}

public class Foo : IFoo<Foo>
{
  // This will not compile
  public Foo(int x)
  {

  }

  #region ITest<Test> Members

  public void SomeMethod()
  {
    throw new NotImplementedException();
  }

  #endregion
}

另一方面,如果你想测试一个类型是否有一个无参数的构造函数,你可以使用反射来做到这一点:

public static class TypeHelper
{
  public static bool HasParameterlessConstructor(Object o)
  {
    return HasParameterlessConstructor(o.GetType());
  }

  public static bool HasParameterlessConstructor(Type t)
  {
    // Usage: HasParameterlessConstructor(typeof(SomeType))
    return t.GetConstructor(new Type[0]) != null;
  }
}

希望这可以帮助。


我会使用接口构造函数来确保某些参数被明确地设置(通过构造函数),所以无参数的ctor并不是我真正想要的。
J
JTtheGeek

我发现解决这个问题的一种方法是将建筑分离成一个单独的工厂。例如,我有一个名为 IQueueItem 的抽象类,我需要一种方法将该对象与另一个对象 (CloudQueueMessage) 相互转换。所以在接口 IQueueItem 我有 -

public interface IQueueItem
{
    CloudQueueMessage ToMessage();
}

现在,我还需要一种方法让我的实际队列类将 CloudQueueMessage 转换回 IQueueItem - 即需要像 IQueueItem objMessage = ItemType.FromMessage 这样的静态构造。相反,我定义了另一个接口 IQueueFactory -

public interface IQueueItemFactory<T> where T : IQueueItem
{
    T FromMessage(CloudQueueMessage objMessage);
}

现在我终于可以在没有 new() 约束的情况下编写我的通用队列类了,这在我的例子中是主要问题。

public class AzureQueue<T> where T : IQueueItem
{
    private IQueueItemFactory<T> _objFactory;
    public AzureQueue(IQueueItemFactory<T> objItemFactory)
    {
        _objFactory = objItemFactory;
    }


    public T GetNextItem(TimeSpan tsLease)
    {
        CloudQueueMessage objQueueMessage = _objQueue.GetMessage(tsLease);
        T objItem = _objFactory.FromMessage(objQueueMessage);
        return objItem;
    }
}

现在我可以创建一个满足我条件的实例

 AzureQueue<Job> objJobQueue = new JobQueue(new JobItemFactory())

希望有一天这可以帮助其他人,显然删除了很多内部代码以尝试显示问题和解决方案


C
Cesar

解决这个问题的一种方法是利用泛型和 new() 约束。

您可以将其表示为工厂类/接口,而不是将构造函数表示为方法/函数。如果您在需要创建类对象的每个调用站点上指定 new() 通用约束,您将能够相应地传递构造函数参数。

对于您的 IDrawable 示例:

public interface IDrawable
{
    void Update();
    void Draw();
}

public interface IDrawableConstructor<T> where T : IDrawable
{
    T Construct(GraphicsDeviceManager manager);
}


public class Triangle : IDrawable
{
    public GraphicsDeviceManager Manager { get; set; }
    public void Draw() { ... }
    public void Update() { ... }
    public Triangle(GraphicsDeviceManager manager)
    {
        Manager = manager;
    }
}


public TriangleConstructor : IDrawableConstructor<Triangle>
{
    public Triangle Construct(GraphicsDeviceManager manager)
    {
        return new Triangle(manager);
    } 
}

现在当你使用它时:

public void SomeMethod<TBuilder>(GraphicsDeviceManager manager)
  where TBuilder: IDrawableConstructor<Triangle>, new()
{
    // If we need to create a triangle
    Triangle triangle = new TBuilder().Construct(manager);

    // Do whatever with triangle
}

您甚至可以使用显式接口实现将所有创建方法集中在一个类中:

public DrawableConstructor : IDrawableConstructor<Triangle>,
                             IDrawableConstructor<Square>,
                             IDrawableConstructor<Circle>
{
    Triangle IDrawableConstructor<Triangle>.Construct(GraphicsDeviceManager manager)
    {
        return new Triangle(manager);
    } 

    Square IDrawableConstructor<Square>.Construct(GraphicsDeviceManager manager)
    {
        return new Square(manager);
    } 

    Circle IDrawableConstructor<Circle>.Construct(GraphicsDeviceManager manager)
    {
        return new Circle(manager);
    } 
}

要使用它:

public void SomeMethod<TBuilder, TShape>(GraphicsDeviceManager manager)
  where TBuilder: IDrawableConstructor<TShape>, new()
{
    // If we need to create an arbitrary shape
    TShape shape = new TBuilder().Construct(manager);

    // Do whatever with the shape
}

另一种方法是使用 lambda 表达式作为初始值设定项。在调用层次结构的早期某个时刻,您将知道需要实例化哪些对象(即,当您创建或获取对 GraphicsDeviceManager 对象的引用时)。获得它后,立即传递 lambda

() => new Triangle(manager) 

到后续方法,以便他们从那时起知道如何创建三角形。如果你不能确定你需要的所有可能的方法,你总是可以创建一个使用反射实现 IDrawable 的类型字典,并在字典中注册上面显示的 lambda 表达式,你可以存储在共享位置或传递给进一步的函数调用。


M
Matthew

通用工厂方法似乎仍然很理想。您会知道工厂需要一个参数,而这些参数恰好被传递给正在实例化的对象的构造函数。

请注意,这只是经过语法验证的伪代码,我在这里可能缺少运行时警告:

public interface IDrawableFactory
{
    TDrawable GetDrawingObject<TDrawable>(GraphicsDeviceManager graphicsDeviceManager) 
              where TDrawable: class, IDrawable, new();
}

public class DrawableFactory : IDrawableFactory
{
    public TDrawable GetDrawingObject<TDrawable>(GraphicsDeviceManager graphicsDeviceManager) 
                     where TDrawable : class, IDrawable, new()
    {
        return (TDrawable) Activator
                .CreateInstance(typeof(TDrawable), 
                                graphicsDeviceManager);
    }

}

public class Draw : IDrawable
{
 //stub
}

public class Update : IDrawable {
    private readonly GraphicsDeviceManager _graphicsDeviceManager;

    public Update() { throw new NotImplementedException(); }

    public Update(GraphicsDeviceManager graphicsDeviceManager)
    {
        _graphicsDeviceManager = graphicsDeviceManager;
    }
}

public interface IDrawable
{
    //stub
}
public class GraphicsDeviceManager
{
    //stub
}

一个可能的用法示例:

    public void DoSomething()
    {
        var myUpdateObject = GetDrawingObject<Update>(new GraphicsDeviceManager());
        var myDrawObject = GetDrawingObject<Draw>(null);
    }

当然,您只希望通过工厂创建实例来保证您始终拥有一个适当初始化的对象。也许使用像 AutoFac 这样的依赖注入框架会有意义; Update() 可以向 IoC 容器“询问”一个新的 GraphicsDeviceManager 对象。


看起来您可以将约束留在接口上,但编译器无法知道工厂将返回实现它的东西,因此只需隐式实现 IDrawableFactory public TDrawable GetDrawingObject<TDrawable>(GraphicsDeviceManager graphicsDeviceManager)
哈哈我在注意到你的之前写了我的回复马特,看起来我们的想法很相似,但我认为你应该使用接口本身的泛型和 where 子句来锁定类型
@JTtheGeek - 我想我理解了部分,但在我看来这会使我的工厂过于僵化,更像是覆盖一个抽象基类。我必须实例化一个全新的工厂对象才能获得底层类型,对吧?这就是为什么我只对 builder 方法施加约束,但我可能会错过标记。也许你可以发布一个你会改变的例子来帮助我更清楚地看到它。需要传递 null 来创建 Draw 对象以满足参数要求,即使 Draw 可能有一个空的默认 ctor 绝对是我的方法的一个缺点。
g
ghord

您可以使用泛型技巧来做到这一点,但它仍然容易受到 Jon Skeet 所写内容的影响:

public interface IHasDefaultConstructor<T> where T : IHasDefaultConstructor<T>, new()
{
}

实现此接口的类必须具有无参数构造函数:

public class A : IHasDefaultConstructor<A> //Notice A as generic parameter
{
    public A(int a) { } //compile time error
}

一些注意事项: 1. 限制来自 new(),检查自己的界面相当有限的使用/过度杀伤。 2. 一旦你声明了构造函数,编译器就会停止自动生成一个无参数的构造函数。有些读者可能无法从您的代码示例中了解这一点。 3. 检查泛型接口的类/实例不方便。 4. Jon Skeet 所说的
M
MauganRa

接口的目的是强制执行某个对象签名。它不应该明确地关心对象在内部是如何工作的。因此,从概念的角度来看,接口中的构造函数并不真正有意义。

虽然有一些选择:

创建一个充当最小默认实现的抽象类。该类应该具有您期望实现类具有的构造函数。

如果您不介意过度杀伤,请使用 AbstractFactory 模式并在工厂类接口中声明一个具有所需签名的方法。

将 GraphicsDeviceManager 作为参数传递给 Update 和 Draw 方法。

使用组合式面向对象编程框架将 GraphicsDeviceManager 传递到需要它的对象部分。在我看来,这是一个非常实验性的解决方案。

你所描述的情况一般都不容易处理。类似的情况是业务应用程序中需要访问数据库的实体。


我不确定我是否理解“因此”。构造函数的实现(它的作用)是内部的,但是构造函数本身是如何内部的呢?它比方法更多或更少“内部”?创建实例时肯定会在外部调用它...
@Joe这是Java做出的设计决定。可以想象创建一种提供强制构造函数的语言。但是,它会使通过构造函数注入的依赖注入变得不可能。此外,由构造函数创建对象是一个非常技术性的问题,并不总是能清晰地转换为业务逻辑。例如,对于类 BlogPost,对象创建(可能在从数据库加载其数据之后)和实际的博客文章创建是两个不同的事件。
r
royatl

你没有。

构造函数是可以实现接口的类的一部分。接口只是类必须实现的方法的契约。


是的,合同将指定实施者需要一个符合此特定签名的 ctor。
L
Lea Hayes

如果可以在接口中定义构造函数,那将非常有用。

鉴于接口是必须以指定方式使用的合同。对于某些情况,以下方法可能是可行的替代方案:

public interface IFoo {

    /// <summary>
    /// Initialize foo.
    /// </summary>
    /// <remarks>
    /// Classes that implement this interface must invoke this method from
    /// each of their constructors.
    /// </remarks>
    /// <exception cref="InvalidOperationException">
    /// Thrown when instance has already been initialized.
    /// </exception>
    void Initialize(int a);

}

public class ConcreteFoo : IFoo {

    private bool _init = false;

    public int b;

    // Obviously in this case a default value could be used for the
    // constructor argument; using overloads for purpose of example

    public ConcreteFoo() {
        Initialize(42);
    }

    public ConcreteFoo(int a) {
        Initialize(a);
    }

    public void Initialize(int a) {
        if (_init)
            throw new InvalidOperationException();
        _init = true;

        b = a;
    }

}

问题在于它不允许我知道我可以将变量传递给这个新项目的构造函数。
@Boris您是否使用反射实例化对象?
K
Kunal

强制某种构造函数的一种方法是在接口中仅声明 Getters,这可能意味着实现类必须有一个方法,最好是构造函数,为其设置值 (privately)。


佚名

虽然您不能在接口中定义构造函数签名,但我觉得值得一提的是,这可能是考虑抽象类的地方。抽象类可以以与接口相同的方式定义未实现的(抽象)方法签名,但也可以具有已实现的(具体)方法和构造函数。

缺点是,因为它是一种类,它不能用于接口可以的任何多继承类型场景。


T
Tom

我使用以下模式使其防弹。

从基类派生他的类的开发人员不能意外地创建一个公共可访问的构造函数

最终类开发人员被迫通过通用的create方法

一切都是类型安全的,不需要铸件

它是 100% 灵活的,可以在任何地方重用,您可以在其中定义自己的基类。

尝试一下,如果不对基类进行修改,就无法破坏它(除非您定义了一个过时的标志而没有将错误标志设置为 true,但即使这样您最终也会收到警告) public abstract class Base其中 TSelf : Base, new() { protected const string FactoryMessage = "Use YourClass.Create(...) instead";公共静态 TSelf 创建(TParameter 参数){ var me = new TSelf();我.初始化(参数);还给我; } [Obsolete(FactoryMessage, true)] protected Base() { } protected virtual void Initialize(TParameter parameter) { } } 公共抽象类 BaseWithConfig: Base 其中 TSelf : BaseWithConfig, new() { 公共 TConfig 配置 { 获取;私人套装; } [Obsolete(FactoryMessage, true)] protected BaseWithConfig() { } protected override void Initialize(TConfig parameter) { this.Config = parameter; } } public class MyService : BaseWithConfig { [Obsolete(FactoryMessage, true)] public MyService() { } } public class Person : Base { [Obsolete(FactoryMessage,true)] public Person() { } protected override void Initialize((string FirstName, string LastName) parameter) { this.FirstName = parameter.FirstName; this.LastName = 参数.LastName; } 公共字符串姓氏 { 获取;私人套装; } 公共字符串名字 { 获取;私人套装; } } [测试] public void FactoryTest() { var notInitilaizedPerson = new Person(); // 由于过时的属性,无法编译。 Person max = Person.Create(("Max", "Mustermann")); Assert.AreEqual("Max",max.FirstName); var service = MyService.Create(("MyUser", "MyPassword")); Assert.AreEqual("MyUser", service.Config.UserName); }

编辑:这是一个基于您的绘图示例的示例,它甚至强制执行接口抽象

        public abstract class BaseWithAbstraction<TSelf, TInterface, TParameter>
        where TSelf : BaseWithAbstraction<TSelf, TInterface, TParameter>, TInterface, new()
    {
        [Obsolete(FactoryMessage, true)]
        protected BaseWithAbstraction()
        {
        }

        protected const string FactoryMessage = "Use YourClass.Create(...) instead";
        public static TInterface Create(TParameter parameter)
        {
            var me = new TSelf();
            me.Initialize(parameter);

            return me;
        }

        protected virtual void Initialize(TParameter parameter)
        {

        }
    }



    public abstract class BaseWithParameter<TSelf, TInterface, TParameter> : BaseWithAbstraction<TSelf, TInterface, TParameter>
        where TSelf : BaseWithParameter<TSelf, TInterface, TParameter>, TInterface, new()
    {
        protected TParameter Parameter { get; private set; }

        [Obsolete(FactoryMessage, true)]
        protected BaseWithParameter()
        {
        }
        protected sealed override void Initialize(TParameter parameter)
        {
            this.Parameter = parameter;
            this.OnAfterInitialize(parameter);
        }

        protected virtual void OnAfterInitialize(TParameter parameter)
        {
        }
    }


    public class GraphicsDeviceManager
    {

    }
    public interface IDrawable
    {
        void Update();
        void Draw();
    }

    internal abstract class Drawable<TSelf> : BaseWithParameter<TSelf, IDrawable, GraphicsDeviceManager>, IDrawable 
        where TSelf : Drawable<TSelf>, IDrawable, new()
    {
        [Obsolete(FactoryMessage, true)]
        protected Drawable()
        {
        }

        public abstract void Update();
        public abstract void Draw();
    }

    internal class Rectangle : Drawable<Rectangle>
    {
        [Obsolete(FactoryMessage, true)]
        public Rectangle()
        {
        }

        public override void Update()
        {
            GraphicsDeviceManager manager = this.Parameter;
            // TODo  manager
        }

        public override void Draw()
        {
            GraphicsDeviceManager manager = this.Parameter;
            // TODo  manager
        }
    }
    internal class Circle : Drawable<Circle>
    {
        [Obsolete(FactoryMessage, true)]
        public Circle()
        {
        }

        public override void Update()
        {
            GraphicsDeviceManager manager = this.Parameter;
            // TODo  manager
        }

        public override void Draw()
        {
            GraphicsDeviceManager manager = this.Parameter;
            // TODo  manager
        }
    }


    [Test]
    public void FactoryTest()
    {
        // doesn't compile because interface abstraction is enforced.
        Rectangle rectangle = Rectangle.Create(new GraphicsDeviceManager());

        // you get only the IDrawable returned.
        IDrawable service = Circle.Create(new GraphicsDeviceManager());
    }

K
Kabali

如果我对 OP 的理解正确,我们希望执行一个契约,其中 GraphicsDeviceManager 总是通过实现类来初始化。我有一个类似的问题,我正在寻找一个更好的解决方案,但这是我能想到的最好的:

将 SetGraphicsDeviceManager(GraphicsDeviceManager gdo) 添加到接口,这样实现类将被迫编写需要从构造函数调用的逻辑。


“这将需要来自构造函数的调用。”不...它不需要调用它只需要 SetGraphicsDeviceManager() 实现,它可能根本不会被调用...
s
srf

从 C# 8.0 开始,接口成员可以声明主体。这称为默认实现。具有主体的成员允许接口为不提供覆盖实现的类和结构提供“默认”实现。此外,从 C# 8.0 开始,接口可能包括:

常量运算符静态构造函数。嵌套类型 静态字段、方法、属性、索引器和事件 使用显式接口实现语法的成员声明。显式访问修饰符(默认访问是公共的)。


这不是OP所要求的。 C# 8 接口中的静态构造函数仅用于类型初始化,就像在类中一样。看问题的最后:“我想定义的是构造函数的签名。就像接口可以定义某个方法的签名一样,接口也可以定义构造函数的签名。”这不是接口中的静态构造函数所提供的。