642 Stimmen

Schnittstelle, die eine Konstruktorsignatur definiert?

Es ist seltsam, dass ich zum ersten Mal mit diesem Problem konfrontiert werde, aber:

Wie definiert man einen Konstruktor in einer C#-Schnittstelle?

編集
Einige Leute wollten ein Beispiel (es ist ein Freizeitprojekt, also ja, es ist ein Spiel)

IDrawable
+Aktualisierung
+Zeichnen

Um sich selbst zu aktualisieren (auf den Rand des Bildschirms zu prüfen usw.) und zu zeichnen, benötigt es immer eine GraphicsDeviceManager . Ich möchte also sicherstellen, dass das Objekt einen Verweis darauf hat. Dies würde in den Konstruktor gehören.

Jetzt, wo ich das aufgeschrieben habe, denke ich, dass ich hier Folgendes umsetze IObservable und die GraphicsDeviceManager sollte die IDrawable ... Es scheint, dass ich entweder das XNA-Framework nicht verstehe, oder dass das Framework nicht sehr gut durchdacht ist.

編集
Es scheint einige Verwirrung über meine Definition von Konstruktor im Zusammenhang mit einer Schnittstelle zu geben. Eine Schnittstelle kann tatsächlich nicht instanziiert werden und braucht daher keinen Konstruktor. Was ich definieren wollte, war eine Signatur für einen Konstruktor. Genau wie eine Schnittstelle eine Signatur einer bestimmten Methode definieren kann, könnte die Schnittstelle die Signatur eines Konstruktors definieren.

3 Stimmen

Anstatt eine Schnittstelle zu haben, die Ihren Konstruktor definiert, haben Sie stattdessen eine Schnittstelle, die Ihre Fabrikmethoden definiert.

0voto

Tom Punkte 41

Ich verwende das folgende Muster, um es kugelsicher zu machen.

  • Ein Entwickler, der seine Klasse von der Basisklasse ableitet, kann nicht versehentlich einen öffentlich zugänglichen Konstruktor erstellen
  • Die Entwickler der letzten Klasse sind gezwungen, die übliche Erstellungsmethode zu durchlaufen
  • Alles ist typsicher, es sind keine Gussteile erforderlich
  • Sie ist 100% flexibel und kann überall wiederverwendet werden, wo Sie Ihre eigene Basisklasse definieren können. Klasse.
  • Probieren Sie es aus, Sie können es nicht zerstören, ohne Änderungen an den Basisklassen vorzunehmen (außer wenn Sie ein veraltetes Flag definieren, ohne dass das Error-Flag auf true gesetzt ist, aber selbst dann erhalten Sie eine Warnung)

        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: Und hier ist ein Beispiel, das auf Ihrem Zeichenbeispiel basiert und sogar die Schnittstellenabstraktion erzwingt

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

-2voto

Kabali Punkte 27

Wenn ich OP richtig verstanden habe, wollen wir einen Vertrag erzwingen, bei dem GraphicsDeviceManager immer von implementierenden Klassen initialisiert wird. Ich hatte ein ähnliches Problem und war auf der Suche nach einer besseren Lösung, aber das ist die beste, die mir einfällt:

Fügen Sie eine SetGraphicsDeviceManager(GraphicsDeviceManager gdo) zur Schnittstelle hinzu, und auf diese Weise werden die implementierenden Klassen gezwungen, eine Logik zu schreiben, die einen Aufruf vom Konstruktor erfordert.

-5voto

srf Punkte 1

Seit C# 8.0 kann ein Schnittstellenmitglied einen Körper deklarieren. Dies wird als Standardimplementierung bezeichnet. Mitglieder mit Body erlauben es der Schnittstelle, eine "Standard"-Implementierung für Klassen und Strukturen bereitzustellen, die keine überschreibende Implementierung bieten. Darüber hinaus kann eine Schnittstelle ab C# 8.0 enthalten:

Konstanten Operatoren Statischer Konstruktor. Verschachtelte Typen Statische Felder, Methoden, Eigenschaften, Indexer und Ereignisse Mitgliederdeklarationen unter Verwendung der expliziten Schnittstellenimplementierungssyntax. Explizite Zugriffsmodifikatoren (der Standardzugriff ist öffentlich).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X