it-swarm.com.de

Schnittstelle, die eine Konstruktorsignatur definiert?

Es ist seltsam, dass dies das erste Mal ist, dass ich dieses Problem getroffen habe, aber:

Wie definieren Sie einen Konstruktor in einer C # -Schnittstelle?

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

Ausweisbar
+ Aktualisieren
+ Zeichnen

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

Nun, da ich dies aufgeschrieben habe, denke ich, dass ich hier IObservable implementiere, und die GraphicsDeviceManager sollte die IDrawable....__ nehmen. Es scheint, dass entweder ich das XNA-Framework nicht bekomme oder das Framework nicht sehr gut durchdacht ist .

Bearbeiten
Es scheint Verwirrung über meine Definition von Konstruktor im Kontext einer Schnittstelle zu geben. Eine Schnittstelle kann in der Tat nicht instanziiert werden, so dass kein Konstruktor erforderlich ist. Was ich definieren wollte, war eine Signatur eines Konstruktors. Genau wie eine Schnittstelle eine Signatur einer bestimmten Methode definieren kann, könnte die Schnittstelle die Signatur eines Konstruktors definieren.

483
Boris Callens

Wie bereits erwähnt, können Sie keine Konstruktoren auf einer Schnittstelle haben. Da dies jedoch ein so hoch bewertetes Ergebnis bei Google etwa sieben Jahre später ist, dachte ich, ich würde mich hier einbringen - insbesondere um zu zeigen, wie Sie eine abstrakte Basisklasse zusammen mit Ihrer vorhandenen Schnittstelle verwenden und möglicherweise den Refactoring-Aufwand reduzieren in der Zukunft für ähnliche Situationen benötigt. In einigen Kommentaren wurde dieses Konzept bereits angedeutet, aber ich dachte, es lohnt sich zu zeigen, wie man es tatsächlich macht.

Sie haben also Ihre Hauptschnittstelle, die bisher so aussieht:

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

Erstellen Sie nun eine abstrakte Klasse mit dem Konstruktor, den Sie erzwingen möchten. Da es seit dem Zeitpunkt, an dem Sie Ihre ursprüngliche Frage geschrieben haben, jetzt verfügbar ist, können wir uns hier ein wenig einfallen lassen und in dieser Situation Generika verwenden, um diese an andere Schnittstellen anzupassen, die möglicherweise dieselbe Funktionalität benötigen, jedoch andere Konstruktoranforderungen haben:

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

    }
}

Jetzt müssen Sie eine neue Klasse erstellen, die sowohl von der IDrawable-Schnittstelle als auch von der abstrakten MustInitialize-Klasse erbt.

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

Dann erstellen Sie einfach eine Instanz von Drawable und Sie können loslegen:

IDrawable drawableService = new Drawable(myGraphicsDeviceManager);

Das Coole daran ist, dass sich die neue Drawable-Klasse genauso verhält, wie wir sie von IDrawable erwarten würden.

Wenn Sie mehr als einen Parameter an den MustInitialize-Konstruktor übergeben müssen, können Sie eine Klasse erstellen, die Eigenschaften für alle Felder definiert, die Sie übergeben müssen.

99
Dan

Das kannst du nicht. Es ist gelegentlich ein Schmerz, aber mit normalen Techniken kann man es sowieso nicht nennen.

In einem Blogbeitrag habe ich statische Schnittstellen vorgeschlagen, was nur bei generischen Typeinschränkungen anwendbar wäre - aber sehr praktisch sein könnte, IMO.

Wenn Sie einen Konstruktor innerhalb einer Schnittstelle definieren könnten , hätten Sie Probleme, Klassen abzuleiten:

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)
    {
    }
}
311
Jon Skeet

Ein sehr später Beitrag, der ein anderes Problem mit Konstrukteuren mit Schnittstellen aufzeigt. (Ich wähle diese Frage, weil sie die klarste Artikulation des Problems aufweist). Angenommen, wir könnten haben:

interface IPerson
{
    IPerson(string name);
}

interface ICustomer
{
    ICustomer(DateTime registrationDate);
}

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

Dabei wird die Implementierung des "Schnittstellenkonstruktors" standardmäßig durch den Typnamen ersetzt.

Machen Sie jetzt eine Instanz:

ICustomer a = new Person("Ernie");

Würden wir sagen, dass der Vertrag ICustomer befolgt wird?

Und was ist damit?

interface ICustomer
{
    ICustomer(string address);
}
133
Gert Arnold

Du kannst nicht.

Schnittstellen definieren Verträge, die andere Objekte implementieren und daher keinen Status haben, der initialisiert werden muss.

Wenn Sie einen Status haben, der initialisiert werden muss, sollten Sie stattdessen eine abstrakte Basisklasse verwenden.

60
Michael

Ich habe auf diese Frage zurückgeschaut und dachte mir, vielleicht nähern wir uns diesem Problem auf die falsche Art und Weise. Schnittstellen sind möglicherweise nicht der richtige Weg, wenn es darum geht, einen Konstruktor mit bestimmten Parametern zu definieren ... aber eine (abstrakte) Basisklasse ist das. 

Wenn Sie eine Basisklasse mit einem Konstruktor erstellen, der die von Ihnen benötigten Parameter akzeptiert, muss jede Klasse, die davon abgeleitet wird, diese bereitstellen. 

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
  {
  }
}
19
Jeroen Landheer

Es ist nicht möglich, eine Schnittstelle zu erstellen, die Konstruktoren definiert, aber es ist is möglich, eine Schnittstelle zu definieren, die einen Typ zwingt, einen parameterlosen Konstruktor zu verwenden, obgleich es eine sehr hässliche Syntax ist, die Generics verwendet ... I Ich bin eigentlich nicht so sicher, dass es wirklich ein gutes Kodierungsmuster ist. 

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
}

Wenn Sie jedoch testen möchten, ob ein Typ einen Konstruktor ohne Parameter enthält, können Sie dies mit Reflektion tun:

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

Hoffe das hilft.

19
Jeroen Landheer

Der generische Fabrikansatz scheint immer noch ideal. Sie würden wissen, dass die Factory einen Parameter benötigt, und es würde gerade so passieren, dass diese Parameter an den Konstruktor des zu instanziierenden Objekts übergeben werden. 

Beachten Sie, dass dies nur Syntax-verifizierter Pseudo-Code ist. Möglicherweise gibt es eine Einschränkung zur Laufzeit, die hier fehlt:

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
}

Ein Beispiel für eine mögliche Verwendung:

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

Zugegeben, Sie möchten nur, dass die Instanzen über die Factory erstellt werden, um sicherzustellen, dass Sie immer über ein entsprechend initialisiertes Objekt verfügen. Vielleicht wäre die Verwendung eines Abhängigkeitsinjektions-Frameworks wie AutoFac sinnvoll. Update () könnte den IoC-Container nach einem neuen GraphicsDeviceManager-Objekt "fragen".

5
Matthew

Eine Möglichkeit, dieses Problem zu lösen, bestand darin, die Konstruktion in eine separate Fabrik zu trennen. Zum Beispiel habe ich eine abstrakte Klasse namens IQueueItem und brauche eine Möglichkeit, dieses Objekt in ein anderes Objekt (CloudQueueMessage) und von einem anderen Objekt zu übersetzen. Also auf der Schnittstelle IQueueItem habe ich - 

public interface IQueueItem
{
    CloudQueueMessage ToMessage();
}

Jetzt brauche ich auch eine Möglichkeit für meine aktuelle Warteschlangenklasse, um eine CloudQueueMessage zurück in ein IQueueItem zu übersetzen - dh die Notwendigkeit einer statischen Konstruktion wie IQueueItem objMessage = ItemType.FromMessage. Stattdessen definierte ich eine andere Schnittstelle IQueueFactory -

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

Jetzt kann ich endlich meine generische Warteschlangenklasse ohne die new () - Einschränkung schreiben, die in meinem Fall das Hauptproblem war.

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

jetzt kann ich eine Instanz erstellen, die die Kriterien für mich erfüllt

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

hoffentlich hilft dies einem anderen, eines Tages, offensichtlich wurde eine Menge interner Code entfernt, um das Problem und die Lösung zu zeigen

5
JTtheGeek

Eine Möglichkeit, dieses Problem zu lösen, besteht darin, Generics und die neue () - Einschränkung zu nutzen.

Anstatt Ihren Konstruktor als Methode/Funktion auszudrücken, können Sie ihn auch als Factory-Klasse/Schnittstelle ausdrücken. Wenn Sie die generische Einschränkung new () für jede Aufrufsite angeben, die ein Objekt Ihrer Klasse erstellen muss, können Sie Konstruktorargumente entsprechend übergeben.

Für Ihr IDrawable-Beispiel:

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

Wenn Sie es jetzt verwenden:

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
}

Sie können sogar alle Erstellungsmethoden in einer einzigen Klasse mithilfe der expliziten Schnittstellenimplementierung konzentrieren:

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

Um es zu benutzen:

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
}

Ein anderer Weg ist die Verwendung von Lambda-Ausdrücken als Initialisierer. Zu einem früheren Zeitpunkt in der Aufrufhierarchie wissen Sie, welche Objekte Sie instanziieren müssen (d. H. Wenn Sie ein Objekt erstellen oder einen Verweis auf Ihr GraphicsDeviceManager-Objekt erhalten). Sobald Sie es haben, übergeben Sie das Lambda

() => new Triangle(manager) 

auf nachfolgende Methoden, damit sie wissen, wie man ein Dreieck von nun an erzeugt. Wenn Sie nicht alle möglichen Methoden ermitteln können, die Sie benötigen, können Sie immer ein Wörterbuch mit Typen erstellen, die IDrawable mit Reflektion implementieren, und den oben gezeigten Lambda-Ausdruck in einem Wörterbuch registrieren, das Sie entweder an einem gemeinsam genutzten Speicherort speichern oder an diesen übergeben können weitere Funktionsaufrufe.

3
Cesar

Sie könnten dies mit dem Generics-Trick tun, aber es ist immer noch anfällig für das, was Jon Skeet schrieb:

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

Die Klasse, die diese Schnittstelle implementiert, muss einen parameterlosen Konstruktor haben:

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

Es wäre sehr nützlich, wenn Konstruktoren in Schnittstellen definiert werden könnten.

Angenommen, eine Schnittstelle ist ein Vertrag, der auf die angegebene Weise verwendet werden muss. Der folgende Ansatz kann für einige Szenarien eine sinnvolle Alternative sein:

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

}
0
Lea Hayes

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

  • Ein Entwickler, der seine Klasse von der Basis ableitet, kann nicht versehentlich einen öffentlich zugänglichen Konstruktor erstellen
  • Die Entwickler der letzten Klasse sind gezwungen, die allgemeine Erstellungsmethode zu verwenden
  • Alles ist typsicher, es sind keine Gussteile erforderlich
  • Es ist 100% flexibel und kann überall dort wiederverwendet werden, wo Sie Ihre eigene Basisklasse definieren können.
  • Probieren Sie es aus, Sie können es nicht brechen, ohne Änderungen an den Basisklassen vorzunehmen (es sei denn, Sie definieren ein veraltetes Flag, ohne dass das Fehlerflag auf true gesetzt ist, aber Sie erhalten trotzdem 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);
    }
    

BEARBEITEN: 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());
    }
0
Tom

Eine Möglichkeit, eine Art Konstruktor zu erzwingen, besteht darin, nur Getters in der Schnittstelle zu deklarieren, was dann bedeuten kann, dass die implementierende Klasse über eine Methode verfügen muss, im Idealfall einen Konstruktor, damit der Wert (privately) gesetzt wird.

0
Kunal

du nicht.

der Konstruktor ist Teil der Klasse, die eine Schnittstelle implementieren kann. Die Schnittstelle ist nur ein Vertrag von Methoden, die die Klasse implementieren muss. 

0
royatl

Der Zweck einer Schnittstelle besteht darin, eine bestimmte Objektsignatur durchzusetzen. Es sollte sich ausdrücklich nicht darum kümmern, wie ein Objekt intern arbeitet. Daher ist ein Konstruktor in einer Schnittstelle aus konzeptioneller Sicht nicht wirklich sinnvoll.

Es gibt jedoch einige Alternativen:

  • Erstellen Sie eine abstrakte Klasse, die als minimale Standardimplementierung fungiert. Diese Klasse sollte die Konstruktoren haben, die Sie für die Implementierung der Klassen erwarten.

  • Wenn Sie den Overkill nicht stört, verwenden Sie das AbstractFactory-Muster und Deklarieren Sie eine Methode in der Factory-Klassenschnittstelle, die die erforderlichen -Signaturen enthält.

  • Übergeben Sie die Variable GraphicsDeviceManager an die Methoden Update und Draw.

  • Verwenden Sie ein kompositorisches objektorientiertes Programmier-Framework, um die GraphicsDeviceManager in den Teil des Objekts zu übergeben, der dies erfordert. Dies ist meiner Meinung nach eine ziemlich experimentelle Lösung.

Die von Ihnen beschriebene Situation ist im Allgemeinen nicht einfach zu handhaben. Ein ähnlicher Fall wären Entitäten in einer Geschäftsanwendung, die Zugriff auf die Datenbank benötigen.

0
MauganRa

Obwohl Sie keine Konstruktorsignatur in einer Schnittstelle definieren können, ist es meiner Meinung nach erwähnenswert, dass dies eine Stelle ist, an der eine abstrakte Klasse betrachtet werden kann. Abstrakte Klassen können unimplementierte (abstrakte) Methodensignaturen auf dieselbe Weise wie ein Interface definieren, sie können jedoch auch (konkrete) Methoden und Konstruktoren implementieren.

Der Nachteil ist, dass es sich um einen Klassentyp handelt, der nicht für die Szenarien mit mehreren Vererbungstypen verwendet werden kann, die eine Schnittstelle bieten kann.

0
IrishLagger

Schnittstellen sind Vertrag Schnittstelle erlaubt keine Felder, d. H. Keine Initialisierung von etwas. Schnittstelle hält nur den Verweis. Schnittstelle erfordert abgeleitete Klasse, um Speicher zu enthalten. 

0