接口定义构造函数签名?

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

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

< p > # EYZ0 < br > 有些人想要一个例子(这是一个自由时间项目,所以是的,这是一个游戏)

< p > IDrawable < br > +更新< br > +画< / p >

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

现在我写下来了,我认为我在这里实现的是IObservableGraphicsDeviceManager应该采取IDrawable… 似乎我没有得到XNA框架,或者框架没有考虑得很好 < p > # EYZ0 < br > 我在接口上下文中对构造函数的定义似乎有些混乱。接口确实不能被实例化,因此不需要构造函数。我想定义的是构造函数的签名。就像接口可以定义某个方法的签名一样,接口也可以定义构造函数的签名

400116 次浏览

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

在一篇博客文章中,我建议静态的接口只能在泛型类型约束中使用——但在我看来,它真的很方便。

关于可以在接口中定义构造函数,你会在派生类时遇到麻烦:

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

你不能。

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

如果需要初始化某些状态,则应该考虑使用抽象基类。

你不。

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

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

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

希望这能有所帮助。

一个非常晚的贡献,展示了接口构造函数的另一个问题。(我选择这个问题是因为它对问题有最清晰的阐述)。假设我们有:

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

我回头看这个问题,心想,也许我们处理这个问题的方法是错误的。当涉及到定义带有特定参数的构造函数时,接口可能不是正确的方法……但是(抽象的)基类是。

如果在基类上创建一个构造函数,该构造函数接受所需的参数,则从基类派生的每个类都需要提供这些参数。

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

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

假设接口是一个必须以指定方式使用的契约。以下方法在某些情况下可能是可行的替代方案:

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


}

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

注意,这只是经过语法验证的伪代码,这里可能忽略了一个运行时警告:

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对象。

我发现解决这个问题的一个方法是将施工分离到一个单独的工厂。例如,我有一个名为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()约束的情况下编写泛型队列类了,在我的例子中,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())

希望有一天这能帮助其他人解决问题,显然,为了显示问题和解决方案,删除了大量内部代码

你可以用泛型来实现,但它仍然容易受到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
}

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

解决这个问题的一种方法是利用泛型和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对象的引用时)。一旦你有了它,就传递

() => new Triangle(manager)

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

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

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

如前所述,在接口上不能有构造函数。但是,由于这是7年后谷歌中排名如此高的结果,我想我应该在这里补充一下——具体来说,是为了展示如何将抽象基类与现有的接口一起使用,并且可能会减少将来在类似情况下所需的重构量。在一些评论中已经暗示了这个概念,但我认为值得展示如何实际做到这一点。

到目前为止你的主界面是这样的:

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构造函数传递多个参数,可以创建一个类,为需要传递的所有字段定义属性。

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

不过也有一些替代方案:

  • 创建一个抽象类,充当最小默认实现。 该类应该具有您期望实现类的构造函数 李。< / p > < / > 如果您不介意过度使用,请使用AbstractFactory模式和 在工厂类接口中声明一个方法,该方法具有所需的 李签名。< / p > < / >

  • GraphicsDeviceManager作为参数传递给UpdateDraw方法。

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

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

我用下面的图案使它防弹。

  • 从基类派生类的开发人员不会意外地创建公共可访问的构造函数
  • 最终的类开发人员被迫使用通用的create方法
  • 一切都是类型安全的,不需要强制类型转换
  • 它是100%灵活的,可以在任何地方重用,在那里你可以定义自己的基础 李课。< / > 尝试一下,如果不修改基类,你就不能打破它(除了 如果你定义了一个过时的标志,没有错误标志设置为true,但即使这样,你最终会得到一个警告)

        public abstract class Base<TSelf, TParameter>
    where TSelf : Base<TSelf, TParameter>, new()
    {
    protected const string FactoryMessage = "Use YourClass.Create(...) instead";
    public static TSelf Create(TParameter parameter)
    {
    var me = new TSelf();
    me.Initialize(parameter);
    
    
    return me;
    }
    
    
    [Obsolete(FactoryMessage, true)]
    protected Base()
    {
    }
    
    
    
    
    
    
    protected virtual void Initialize(TParameter parameter)
    {
    
    
    }
    }
    
    
    public abstract class BaseWithConfig<TSelf, TConfig>: Base<TSelf, TConfig>
    where TSelf : BaseWithConfig<TSelf, TConfig>, new()
    {
    public TConfig Config { get; private set; }
    
    
    [Obsolete(FactoryMessage, true)]
    protected BaseWithConfig()
    {
    }
    protected override void Initialize(TConfig parameter)
    {
    this.Config = parameter;
    }
    }
    
    
    public class MyService : BaseWithConfig<MyService, (string UserName, string Password)>
    {
    [Obsolete(FactoryMessage, true)]
    public MyService()
    {
    }
    }
    
    
    public class Person : Base<Person, (string FirstName, string LastName)>
    {
    [Obsolete(FactoryMessage,true)]
    public Person()
    {
    }
    
    
    protected override void Initialize((string FirstName, string LastName) parameter)
    {
    this.FirstName = parameter.FirstName;
    this.LastName = parameter.LastName;
    }
    
    
    public string LastName { get; private set; }
    
    
    public string FirstName { get; private set; }
    }
    
    
    
    
    
    
    [Test]
    public void FactoryTest()
    {
    var notInitilaizedPerson = new Person(); // doesn't compile because of the obsolete attribute.
    Person max = Person.Create(("Max", "Mustermann"));
    Assert.AreEqual("Max",max.FirstName);
    
    
    var service = MyService.Create(("MyUser", "MyPassword"));
    Assert.AreEqual("MyUser", service.Config.UserName);
    }
    

EDIT: And here is an example based on your drawing example that even enforces interface abstraction

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