ChatGPT解决这个技术问题 Extra ChatGPT

Interface defining a constructor signature?

It's weird that this is the first time I've bumped into this problem, but:

How do you define a constructor in a C# interface?

Edit Some people wanted an example (it's a free time project, so yes, it's a game)

IDrawable +Update +Draw

To be able to Update (check for edge of screen etc) and draw itself it will always need a GraphicsDeviceManager. So I want to make sure the object has a reference to it. This would belong in the constructor.

Now that I wrote this down I think what I'm implementing here is IObservable and the GraphicsDeviceManager should take the IDrawable... It seems either I don't get the XNA framework, or the framework is not thought out very well.

Edit There seems to be some confusion about my definition of constructor in the context of an interface. An interface can indeed not be instantiated so doesn't need a constructor. What I wanted to define was a signature to a constructor. Exactly like an interface can define a signature of a certain method, the interface could define the signature of a constructor.

Instead of having an interface defining your constructor, have an interface defining your factory methods instead.

k
kevinarpe

You can't. It's occasionally a pain, but you wouldn't be able to call it using normal techniques anyway.

In a blog post I've suggested static interfaces which would only be usable in generic type constraints - but could be really handy, IMO.

One point about if you could define a constructor within an interface, you'd have trouble deriving classes:

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

I could see problems indeed, but the same goes for all other methods you define. Usually NotSupportedException is the only way out.
@boris: The difference is that there's always something to be called with normal inheritance, guaranteed by the compiler. In this case there's something which "ought" to be there but isn't.
Yeah but what's wrong with that, there's no suitable Bar constructor because it doesn't satisfy the interface properly. That'd be like saying you can't define methods in interfaces because if you don't implement it, it won't work.
@Gravitas: Useful or not, it's certainly not available today. I suspect if this feature were ever to show up, it would require rather more careful design than we can do in comments :)
@user1721649: There are plenty of places where I've wanted this - almost always with generic constraints. Basically, you want to be able to call a constructor within a generic method, to create an instance of the generic type implementing some interface. It really would be useful.
D
Dan

As already well noted, you can't have constructors on an Interface. But since this is such a highly ranked result in Google some 7 years later, I thought I would chip in here - specifically to show how you could use an abstract base class in tandem with your existing Interface and maybe cut down on the amount of refactoring needed in the future for similar situations. This concept has already been hinted at in some of the comments but I thought it would be worth showing how to actually do it.

So you have your main interface that looks like this so far:

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

Now create an abstract class with the constructor you want to enforce. Actually, since it's now available since the time you wrote your original question, we can get a little fancy here and use generics in this situation so that we can adapt this to other interfaces that might need the same functionality but have different constructor requirements:

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

    }
}

Now you'll need to create a new class that inherits from both the IDrawable interface and the MustInitialize abstract class:

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

Then just create an instance of Drawable and you're good to go:

IDrawable drawableService = new Drawable(myGraphicsDeviceManager);

The cool thing here is that the new Drawable class we created still behaves just like what we would expect from an IDrawable.

If you need to pass more than one parameter to the MustInitialize constructor, you can create a class that defines properties for all of the fields you'll need to pass in.


It is important to emphasize that you can't create a "MustInitialize" class to cover every case, because C# does not allow multiple inheritance. Meaning that if your class inherits an abstract class cannot inherits another class too.
That is true @Skarlot, it's not a silver bullet solution. In the problem you point out though, hopefully it would make sense to directly modify the abstract class that you are already inheriting. But there are still situations where that isn't possible and/or appropriate, so a deeper design pattern would be required.
In C# you can only inheret from one base class, so inhereting from MustInitialize prevents you from inhereting from other classes. An alternative would be to have a method that acts like a constuctor - setting the classes properties and fields, maybe with sentinels that prevent it being called twice successfully, or the class to be used until that method is called.
@andrew pate - As Skarllot has pointed out, that is true about multiple inheritance, but it is not a concern in this specific case from the OP. Although what you proposed would work, I wouldn't recommend ever writing a class that requires a public method to be called in order to be used properly. If you're not familiar with the term, google "Temporal Coupling".
@Dan, good point about temporal coupling. A hack could be to put in a sutably named reminder method, which is not expected to contain an implementation for example: ExpectedConstructorSignature_Drawable(GraphicsDeviceManager graphicsDeviceManager)
A
Ajay

A very late contribution demonstrating another problem with interfaced constructors. (I choose this question because it has the clearest articulation of the problem). Suppose we could have:

interface IPerson
{
    IPerson(string name);
}

interface ICustomer
{
    ICustomer(DateTime registrationDate);
}

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

Where by convention the implementation of the "interface constructor" is replaced by the type name.

Now make an instance:

ICustomer a = new Person("Ernie");

Would we say that the contract ICustomer is obeyed?

And what about this:

interface ICustomer
{
    ICustomer(string address);
}

if you have a method by the same signature and name in ICustomer and IPerson, this problem is there also. I do not see how this helps. Interfaces are not for "only my definition". It is for "include me at any cost"
@nawfal The point is that an interface never demands that a method is executed, only that it should exist. It can never guarantee state. Contrary, a "constructor interface" does demand that something be done (executed) when an object is constructed. This can never be guaranteed when there are different interfaces.
@GertArnold a method does its job, while a constructor does its. I do not know understand what is the difference here. Interfaces make a contract that "my implementation should be there", not like "mine should be the only implementation". I would say for consistency, this should be valid for constructors, methods, properties
I don't see how this is a problem, one could simply make a rule that only allows chaining interfaces with identical constructor signatures. It would be the same behavior as if interface A implements "foo : int" and interface B implements "foo : string", they are just not compatible.
It reminds me of c++ multiple inheritence problem without virtual inheritance
M
Michael

You can't.

Interfaces define contracts that other objects implement and therefore have no state that needs to be initialized.

If you have some state that needs to be initialized, you should consider using an abstract base class instead.


Why cant a contract have a state?
Because the contract binds you to provide a certain behaviour. How interfaces are used implies extracting common behaviours, and that is not dependent on state (which would then be an implementation detail).
Seems we could use a separate mechanic like a "contract" apart from interfaces for requiring an object to implement certain features like constructors and static methods/properties that will not be called through the contract (like you would if it were an interface). Feature suggestion for next .Net version?
J
Jeroen Landheer

I was looking back at this question and I thought to myself, maybe we are aproaching this problem the wrong way. Interfaces might not be the way to go when it concerns defining a constructor with certain parameters... but an (abstract) base class is.

If you create a base class with a constructor on there that accepts the parameters you need, every class that derrives from it needs to supply them.

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

This is how I solved this issue as well. My interface defines what the class needs to be able to do, but my base abstract class enforces the constructor component of it.
J
Jeroen Landheer

It is not possible to create an interface that defines constructors, but it is possible to define an interface that forces a type to have a paramerterless constructor, though be it a very ugly syntax that uses generics... I am actually not so sure that it is really a good coding pattern.

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
}

On the other hand, if you want to test if a type has a paramerterless constructor, you can do that using reflection:

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

Hope this helps.


I would use the interface constructor to make sure some arguments are defenatly set (through the constructor) so a parameterless ctor is not really what I'm looking for.
J
JTtheGeek

One way to solve this problem i found is to seperate out the construction into a seperate factory. For example I have an abstract class called IQueueItem, and I need a way to translate that object to and from another object (CloudQueueMessage). So on the interface IQueueItem i have -

public interface IQueueItem
{
    CloudQueueMessage ToMessage();
}

Now, I also need a way for my actual queue class to translate a CloudQueueMessage back to a IQueueItem - ie the need for a static construction like IQueueItem objMessage = ItemType.FromMessage. Instead I defined another interface IQueueFactory -

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

Now I can finally write my generic queue class without the new() constraint which in my case was the main issue.

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

now I can create an instance that satisfies the criteria for me

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

hopefully this helps someone else out someday, obviously a lot of internal code removed to try to show the problem and solution


C
Cesar

One way to solve this problem is to leverage generics and the new() constraint.

Instead of expressing your constructor as a method/function, you can express it as a factory class/interface. If you specify the new() generic constraint on every call site that needs to create an object of your class, you will be able to pass constructor arguments accordingly.

For your IDrawable example:

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

Now when you use it:

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
}

You can even concentrate all creation methods in a single class using explicit interface implementation:

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

To use it:

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
}

Another way is by using lambda expressions as initializers. At some point early in the call hierarchy, you will know which objects you will need to instantiate (i.e. when you are creating or getting a reference to your GraphicsDeviceManager object). As soon as you have it, pass the lambda

() => new Triangle(manager) 

to subsequent methods so they will know how to create a Triangle from then on. If you can't determine all possible methods that you will need, you can always create a dictionary of types that implement IDrawable using reflection and register the lambda expression shown above in a dictionary that you can either store in a shared location or pass along to further function calls.


M
Matthew

The generic factory approach still seems ideal. You would know that the factory requires a parameter, and it would just so happen that those parameters are passed along to the constructor of the object being instantiated.

Note, this is just syntax verified pseudo code, there may be a run-time caveat I'm missing here:

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
}

An example of possible usage:

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

Granted, you'd only want the create instances via the factory to guarantee you always have an appropriately initialized object. Perhaps using a dependency injection framework like AutoFac would make sense; Update() could "ask" the IoC container for a new GraphicsDeviceManager object.


It looks like you can leave the constraints on the interface, but there is no way for the compiler to know that the factory is going to return something the implements it, so just implicitly implement IDrawableFactory public TDrawable GetDrawingObject<TDrawable>(GraphicsDeviceManager graphicsDeviceManager)
Hahah I wrote my response before noticing yours Matt, looks like we think alike but I think you should use the generic on the interface itself with the where clause to lock the type
@JTtheGeek - I think I understand partially, but it seems to me it would make my factory too rigid, more like overriding an abstract base class. I would have to instantiate a completely new factory object to get at the underlying types, right? This is why I only put the constraints on the builder method, but I may be missing the mark. Perhaps you could post an example of what you'd change to help me see it more clearly. Needing to pass null to create a Draw object to satisfy the parameter requirements even though Draw may have an empty, default ctor is definitely a drawback to my approach.
g
ghord

You could do this with generics trick, but it still is vulnerable to what Jon Skeet wrote:

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

Class that implements this interface must have parameterless constructor:

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

A few caveats: 1. The restriction comes from new(), checking for the own interface of rather limited use / overkill. 2. As soon as you declare constructors, the compiler stops auto-generating a parameterless constructor. Some readers may not catch on to that from your code-example. 3. Checking classes / instances for generic interfaces is inconvenient. 4. What Jon Skeet said
M
MauganRa

The purpose of an interface is to enforce a certain object signature. It should explicitly not be concerned with how an object works internally. Therefore, a constructor in an interface does not really make sense from a conceptual point of view.

There are some alternatives though:

Create an abstract class that acts as a minimal default implementation. That class should have the constructors you expect implementing classes to have.

If you don't mind the overkill, use the AbstractFactory pattern and declare a method in the factory class interface that has the required signatures.

Pass the GraphicsDeviceManager as a parameter to the Update and Draw methods.

Use a Compositional Object Oriented Programming framework to pass the GraphicsDeviceManager into the part of the object that requires it. This is a pretty experimental solution in my opinion.

The situation you describe is not easy to handle in general. A similar case would be entities in a business application that require access to the database.


I'm not sure I understand the 'therefore'. The implementation of a constructor (what it does) is internal, but how is a constructor itself internal? How is it any more or less 'internal' than a method? It's certainly called externally when creating an instance...
@Joe It is a design decision made by Java. It would be conceivable to create a language that offers mandatory constructors. However, it would make dependency injection via constructor injection impossible. Also, object creation by constructor is a very technical concern that not always translate cleanly to business logic. For example, for a class BlogPost, object creation (possibily after loading its data from the database) and actual blog post creation are two different events.
r
royatl

you don't.

the constructor is part of the class that can implement an interface. The interface is just a contract of methods the class must implement.


Yes and the contract would specify that the implementor needs a ctor that complies to this specific signature.
L
Lea Hayes

It would be very useful if it were possible to define constructors in interfaces.

Given that an interface is a contract that must be used in the specified way. The following approach might be a viable alternative for some scenarios:

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

}

The problem with that is that it doesn't allow me to know that I can pass a variable to the constructor of this new item.
@Boris Are you instantiating objects using reflection?
K
Kunal

One way to force some sort of constructor is to declare only Getters in interface, which could then mean that the implementing class must have a method, ideally a constructor, to have the value set (privately) for it.


佚名

While you can't define a constructor signature in an interface, I feel it's worth mentioning that this may be a spot to consider an abstract class. Abstract classes can define unimplemented (abstract) method signatures in the same way as an interface, but can also have implemented (concrete) methods and constructors.

The downside is that, because it is a type of class, it cannot be used for any of the multiple inheritance type scenarios that an interface can.


T
Tom

I use the following pattern to make it bulletproof.

A developer who derives his class from the base can't accidentally create a public accessible constructor

The final class developer are forced to go through the common create method

Everything is type-safe, no castings are required

It's 100% flexible and can be reused everywhere, where you can define your own base class.

Try it out you can't break it without making modifications to the base classes (except if you define an obsolete flag without error flag set to true, but even then you end up with a warning) public abstract class Base where TSelf : Base, 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: Base where TSelf : BaseWithConfig, 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 { [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 = 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());
    }

K
Kabali

If I understood OP correctly, we want to enforce a contract where GraphicsDeviceManager is always initialised by implementing classes. I had a similar problem and I was looking for a better solution, but this is the best I can think of:

Add a SetGraphicsDeviceManager(GraphicsDeviceManager gdo) to the interface, and that way the implementing classes will be forced to write a logic which will be require a call from constructor.


"which will be require a call from constructor." nope... it will not require call it will only require SetGraphicsDeviceManager() implementation which may still be not called at all...
s
srf

Beginning with C# 8.0, an interface member may declare a body. This is called a default implementation. Members with bodies permit the interface to provide a "default" implementation for classes and structs that don't provide an overriding implementation. In addition, beginning with C# 8.0, an interface may include:

Constants Operators Static constructor. Nested types Static fields, methods, properties, indexers, and events Member declarations using the explicit interface implementation syntax. Explicit access modifiers (the default access is public).


That's not what the OP is asking for. A static constructor in a C# 8 interface is just for type initialization, as it is in a class. Look at the end of the question: "What I wanted to define was a signature to a constructor. Exactly like an interface can define a signature of a certain method, the interface could define the signature of a constructor." That is not what a static constructor in an interface provides.