Interfaz que define una firma de constructor

Es extraño que esta sea la primera vez que me topé con este problema, pero:

¿Cómo se define un constructor en una interfaz C #?

Editar
Algunas personas querían un ejemplo (es un proyecto de tiempo libre, así que sí, es un juego)

IDrawable
+ Actualización
+ Draw

Para poder actualizar (verificar el borde de la pantalla, etc.) y dibujar, siempre necesitará un GraphicsDeviceManager . Así que quiero asegurarme de que el objeto tenga una referencia. Esto debería pertenecer al constructor.

Ahora que lo escribí, creo que lo que estoy implementando aquí es IObservable y GraphicsDeviceManager debería tomar el IDrawable … Parece que o no obtengo el framework XNA, o el framework no está pensado muy bien.

Editar
Parece haber cierta confusión sobre mi definición de constructor en el contexto de una interfaz. De hecho, una interfaz no se puede instanciar, por lo que no necesita un constructor. Lo que quería definir era una firma para un constructor. Exactamente como una interfaz puede definir una firma de un determinado método, la interfaz podría definir la firma de un constructor.

Como ya se señaló, no puede tener constructores en una interfaz. Pero dado que este es un resultado tan altamente calificado en Google unos 7 años después, pensé en ingresar aquí, específicamente para mostrar cómo se puede usar una clase base abstracta en conjunto con su Interfaz existente y quizás reducir la cantidad de refactorización. necesario en el futuro para situaciones similares. Este concepto ya ha sido insinuado en algunos de los comentarios, pero pensé que valdría la pena mostrar cómo hacerlo realmente.

Entonces tienes tu interfaz principal que se ve así hasta ahora:

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

Ahora crea una clase abstracta con el constructor que quieras aplicar. En realidad, dado que ahora está disponible desde el momento en que escribiste tu pregunta original, podemos hacernos un poco de imaginación y usar generics en esta situación para adaptarlo a otras interfaces que puedan necesitar la misma funcionalidad pero que tengan diferentes requisitos de construcción:

 public abstract class MustInitialize { public MustInitialize(T parameters) { } } 

Ahora necesitará crear una nueva clase que herede tanto de la interfaz de IDrawable como de la clase abstracta MustInitialize:

 public class Drawable : MustInitialize, 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 } } 

Luego solo crea una instancia de Drawable y listo:

 IDrawable drawableService = new Drawable(myGraphicsDeviceManager); 

Lo bueno de esto es que la nueva clase Drawable que creamos todavía se comporta como lo que esperaríamos de un IDrawable.

Si necesita pasar más de un parámetro al constructor de MustInitialize, puede crear una clase que defina propiedades para todos los campos en los que deberá pasar.

No puedes. En ocasiones es un dolor, pero de todos modos no podrías llamarlo usando las técnicas normales.

En una publicación de blog, sugerí interfaces estáticas que solo se podrían usar en restricciones de tipo genérico, pero podrían ser realmente útiles, IMO.

Un punto acerca de si podría definir un constructor dentro de una interfaz, tendría problemas para derivar clases:

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

Una contribución muy tardía que demuestra otro problema con los constructores interconectados. (Elijo esta pregunta porque tiene la articulación más clara del problema). Supongamos que pudiéramos tener:

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

Donde por convención la implementación del “constructor de interfaz” es reemplazada por el nombre de tipo.

Ahora haz una instancia:

 ICustomer a = new Person("Ernie"); 

¿ ICustomer que el contrato ICustomer es obedecido?

Y que tal esto?

 interface ICustomer { ICustomer(string address); } 

No puedes.

Las interfaces definen contratos que implementan otros objetos y, por lo tanto, no tienen ningún estado que deba inicializarse.

Si tiene algún estado que deba inicializarse, debe considerar usar una clase base abstracta en su lugar.

No es posible crear una interfaz que defina constructores, pero es posible definir una interfaz que obligue a un tipo a tener un constructor sin parámetros, aunque sea una syntax muy fea que use generics … De hecho, no estoy tan seguro de que es realmente un buen patrón de encoding.

 public interface IFoo where T : new() { void SomeMethod(); } public class Foo : IFoo { // This will not compile public Foo(int x) { } #region ITest Members public void SomeMethod() { throw new NotImplementedException(); } #endregion } 

Por otro lado, si desea probar si un tipo tiene un constructor sin parámetros, puede hacerlo utilizando la reflexión:

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

Espero que esto ayude.

Estaba mirando hacia atrás a esta pregunta y pensé para mí mismo, tal vez estamos abordando este problema de la manera incorrecta. Las interfaces pueden no ser el camino a seguir cuando se trata de definir un constructor con ciertos parámetros … pero una clase base (abstracta) sí lo es.

Si crea una clase base con un constructor allí que acepta los parámetros que necesita, todas las clases que derivan de ella deben proporcionarlos.

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

El enfoque genérico de fábrica todavía parece ideal. Sabría que la fábrica requiere un parámetro, y sucedería que esos parámetros se transfieren al constructor del objeto que se está instanciando.

Tenga en cuenta que esto es solo pseudo código verificado por syntax, puede que me falte una advertencia de tiempo de ejecución aquí:

 public interface IDrawableFactory { TDrawable GetDrawingObject(GraphicsDeviceManager graphicsDeviceManager) where TDrawable: class, IDrawable, new(); } public class DrawableFactory : IDrawableFactory { public TDrawable GetDrawingObject(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 } 

Un ejemplo de uso posible:

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

De acuerdo, solo querría crear instancias a través de la fábrica para garantizar que siempre tenga un objeto inicializado apropiadamente. Quizás usar un marco de dependency injections como AutoFac tendría sentido; Update () podría “pedir” al contenedor IoC un nuevo objeto GraphicsDeviceManager.

Una forma de resolver este problema que encontré es separar la construcción en una fábrica separada. Por ejemplo, tengo una clase abstracta llamada IQueueItem, y necesito una forma de traducir ese objeto ay desde otro objeto (CloudQueueMessage). Entonces en la interfaz IQueueItem tengo –

 public interface IQueueItem { CloudQueueMessage ToMessage(); } 

Ahora, también necesito una forma para que mi clase de cola real traduzca un CloudQueueMessage a un IQueueItem, es decir, la necesidad de una construcción estática como IQueueItem objMessage = ItemType.FromMessage. En su lugar, definí otra interfaz IQueueFactory –

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

Ahora puedo finalmente escribir mi clase de cola genérica sin la nueva restricción () que en mi caso era el problema principal.

 public class AzureQueue where T : IQueueItem { private IQueueItemFactory _objFactory; public AzureQueue(IQueueItemFactory objItemFactory) { _objFactory = objItemFactory; } public T GetNextItem(TimeSpan tsLease) { CloudQueueMessage objQueueMessage = _objQueue.GetMessage(tsLease); T objItem = _objFactory.FromMessage(objQueueMessage); return objItem; } } 

ahora puedo crear una instancia que satisfaga los criterios para mí

  AzureQueue objJobQueue = new JobQueue(new JobItemFactory()) 

esperemos que esto ayude a alguien más a salir algún día, obviamente se eliminó un montón de código interno para tratar de mostrar el problema y la solución

Una forma de resolver este problema es aprovechar los generics y la nueva () restricción.

En lugar de express su constructor como un método / función, puede expresslo como una clase / interfaz de fábrica. Si especifica la nueva () restricción genérica en cada sitio de llamada que necesita crear un objeto de su clase, podrá pasar argumentos de constructor en consecuencia.

Para su ejemplo de IDrawable:

 public interface IDrawable { void Update(); void Draw(); } public interface IDrawableConstructor 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 { public Triangle Construct(GraphicsDeviceManager manager) { return new Triangle(manager); } } 

Ahora cuando lo usas:

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

Incluso puede concentrar todos los métodos de creación en una sola clase mediante la implementación explícita de la interfaz:

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

Para usarlo:

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

Otra forma es mediante el uso de expresiones lambda como inicializadores. En algún momento al principio de la jerarquía de llamadas, sabrá qué objetos necesitará para crear instancias (es decir, cuando está creando u obteniendo una referencia a su objeto GraphicsDeviceManager). Tan pronto como lo tengas, pasa el lambda

 () => new Triangle(manager) 

a los métodos posteriores para que sepan cómo crear un triángulo a partir de ese momento. Si no puede determinar todos los métodos posibles que necesitará, siempre puede crear un diccionario de tipos que implementen IDrawable usando reflection y registrar la expresión lambda que se muestra arriba en un diccionario que puede almacenar en una ubicación compartida o pasar a otras funciones llamadas

Podrías hacer esto con el truco de los generics, pero aún es vulnerable a lo que Jon Skeet escribió:

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

La clase que implementa esta interfaz debe tener un constructor sin parámetros:

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

tu no

el constructor es parte de la clase que puede implementar una interfaz. La interfaz es solo un contrato de métodos que la clase debe implementar.

Sería muy útil si fuera posible definir constructores en las interfaces.

Dado que una interfaz es un contrato que debe usarse de la manera especificada. El siguiente enfoque podría ser una alternativa viable para algunos escenarios:

 public interface IFoo { ///  /// Initialize foo. ///  ///  /// Classes that implement this interface must invoke this method from /// each of their constructors. ///  ///  /// Thrown when instance has already been initialized. ///  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; } } 

Una forma de forzar a algún tipo de constructor es declarar solo Getters en la interfaz, lo que podría significar que la clase implementadora debe tener un método, idealmente un constructor, para tener el valor establecido ( private ) para él.

Si bien no puede definir una firma de constructor en una interfaz, creo que vale la pena mencionar que este puede ser un lugar para considerar una clase abstracta. Las clases abstractas pueden definir las firmas de métodos no implementados (abstractos) de la misma manera que una interfaz, pero también pueden tener implementados (concretos) métodos y constructores.

La desventaja es que, debido a que es un tipo de clase, no se puede usar para ninguno de los múltiples escenarios de tipo de herencia que puede tener una interfaz.

El propósito de una interfaz es hacer cumplir una determinada firma de objeto. No debería preocuparse explícitamente de cómo funciona un objeto internamente. Por lo tanto, un constructor en una interfaz realmente no tiene sentido desde un punto de vista conceptual.

Sin embargo, hay algunas alternativas:

  • Cree una clase abstracta que actúe como una implementación predeterminada mínima. Esa clase debe tener los constructores que espera que tenga la implementación de clases.

  • Si no le molesta la exageración, use el patrón AbstractFactory y declare un método en la interfaz de clase de fábrica que tenga las firmas necesarias.

  • Pase el GraphicsDeviceManager como parámetro a los métodos Update y Draw .

  • Use una estructura de Progtwigción Orientada a Objetos para pasar el GraphicsDeviceManager a la parte del objeto que lo requiere. Esta es una solución bastante experimental en mi opinión.

La situación que describes no es fácil de manejar en general. Un caso similar sería entidades en una aplicación comercial que requieren acceso a la base de datos.

Si entendí OP correctamente, queremos aplicar un contrato donde GraphicsDeviceManager siempre se inicialice implementando clases. Tuve un problema similar y estaba buscando una solución mejor, pero esto es lo mejor que puedo pensar:

Agregue un SetGraphicsDeviceManager (GraphicsDeviceManager gdo) a la interfaz, y de esa manera las clases de implementación se verán obligadas a escribir una lógica que requerirá una llamada del constructor.