Arbeiten mit .NET: OOP/ Verknüpfung/ Interface/ Grundlagen

Eine Schnittstelle (engl. Interface) ist in der Softwareentwicklung ein Minimal-Vertrag, an den sich alle Partner, die diesen Vertrag eingehen, zwingend zu halten haben.

Wikipedia hat einen Artikel zum Thema:

ImplementierungBearbeiten

Implementiert eine Klasse eine bestimmte Schnittstelle, garantiert sie, dass alle Bedingungen, die diese Schnittstelle aufstellt, eingehalten werden.

interface ISchnittstelle
{
  int Berechne(int a, int b);
}

Diese Schnittstelle verlangt, dass die implementierende Klasse eine Methode besitzt, die Berechne heißt und einen Ganzzahlwert zurückgibt. Eine entsprechende Implementierung könnte also etwa so aussehen:

class Klasse1 : ISchnittstelle
{
 public int Berechne(int a, int b)
 {
   return a + b;
 }
}

Eine andere Implementierung dieser Schnittstelle könnte so aussehen:

class Klasse2 : ISchnittstelle
{
  public int Berechne(int a, int  b)
  {
    return a * b;
  }
}

Aber was passiert, wenn die Klasse, die die Schnittstelle ISchnittstelle implementieren soll, bereits eine Methode mit einer identischen Signatur hat? Kann man dann die Schnittstelle nicht benutzen?

Doch, man kann. In diesem Fall muss man die Schnittstelle explizit implementieren:

class Klasse3 : ISchnittstelle
{
  // Diese Methode ist bereits in unserer Klasse eingebaut.
  // Sie hat die gleiche Signatur, 
  // wie die Methode der Schnittstelle.
  public int Berechne(int a, int b)
  {
    return a - b;
  }

  #region Implementierung der Schnittstelle ISchnittstelle
  int ISchnittstelle.Berechne(int a, int b)
  { 
    return a * b;
  }
  #endregion
}

Und wie kommt man nun an diese Methode heran?

// Da die Klasse zusichert, dass sie die Schnittstelle 
// sauber implementiert, holen wir uns eine Instanz.
ISchnittstelle berechnung = new Klasse3();

// Jetzt können wir die Methode aufrufen.
// Da wir über die Schnittstelle zugreifen, weiß die Runtime, 
// dass wir die explizit implementierte Methode 
// der Schnittstelle haben wollen.
int ergebnis = berechnung.Berechne(3, 5);

Das ist aber unangenehm, wenn wir bereits ein Objekt haben, und das nicht verlieren wollen. Deshalb gibt es noch eine alternative Möglichkeit: Wir "casten" das Objekt:

// Objekt der Klasse holen
Klasse3 klasse = new Klasse3();

// Hier arbeiten wir mit dem Objekt...

// ... und jetzt möchten wir gern an diese Methode herankommen.
// Wahlweise können wir das "on the fly" machen ...
int ergebnis = ((ISchnittstelle)klasse).Berechne(3, 5);

// ... oder, wenn wir die Methode häufiger brauchen,
// einen Verweis erzeugen ...
ISchnittstelle interface = (ISchnittstelle)klasse;
// ... den wir beliebig oft benutzen können.
int ergebnis2 = interface.Berechne(3, 5);

Mehrere Schnittstellen implementierenBearbeiten

Anders als bei der Vererbung von Klassen, ist es hier durchaus möglich, mehrere Schnittstellen gleichzeitig einzubinden. Damit sichert die implementierende Klasse zu, dass sie alle Vertragsbedingungen der eingebundenen Schnittstellen erfüllt.

Implizite ImplementierungBearbeiten

public interface I1
{
  void A();
}
public interface I2
{
  void B();
}

// Implizite Implementierung der Schnittstellen
public class Klasse : I1, I2
{
  void A() {...} // A aus Schnittstelle I1
  void B() {...} // B aus Schnittstelle I2
}

PraxisbeispielBearbeiten

C#-Quelltext
interface IAdresse
{
   // Methoden vertraglich festlegen
   void SetzeAdresse(string ort, string plz, string strasse, string hausnummer);
 
   // Eigenschaften vertraglich festlegen</span>
   string Ort { get; }
   string PLZ { get; }
   string Strasse { get; }
   string Hausnummer { get; }
}

Die Schnittstelle IAdresse legt in ihren Vertragsbedingungen fest, dass eine Methode namens SetzeAdresse implementiert werden muss. Außerdem sollen die Eigenschaften Ort, PLZ, Strasse und Hausnummer als Nur-Lese-Eigenschaften implementiert werden.

C#-Quelltext
interface IName
{
   // Methoden vertraglich festlegen
 
   // Eigenschaften vertraglich festlegen
   string Nachname { get; set; }
   string Vorname { get; set; }
   string VollerName { get; }
}

Die Schnittstelle IName erzwingt drei Eigenschaften : Nachname, Vorname und die Nur-Lese-Eigenschaft VollerName.

Eine entsprechende Implementierung der Schnittstellen in einer Klasse sieht demzufolge etwa so aus:

C#-Quelltext
class Mitarbeiter : IAdresse, IName
{
   // Variablen, deren Aufgabe das Auffangen
   // der übergebenen Daten ist.
   private string m_Ort;
   private string m_PLZ;
   private string m_Strasse;
   private string m_Hausnummer;
 
   private string m_Nachname;
   private string m_Vorname;
   
   #region Implementierung der Schnittstelle IAdresse
 
   public void SetzeAdresse(string ort, string plz, string strasse, string hausnummer)
   {
     m_Ort = ort;
     m_PLZ = plz;
     m_Strasse = strasse;
     m_Hausnummer = hausnummer;
   }
 
   //
   // Eigenschaften der Schnittstelle IAdresse
   //
 
   // Nur-Lese-Eigenschaft Ort
   public string Ort
   { 
     get { return m_Ort; }
   }
 
   // Nur-Lese-Eigenschaft PLZ
   public string PLZ
   { 
     get { return m_PLZ; }
   }
 
   // Nur-Lese-Eigenschaft Strasse
   public string Strasse
   { 
     get { return m_Strasse; }
   }
 
   // Nur-Lese-Eigenschaft Hausnummer
   public string Hausnummer
   { 
     get { return m_Hausnummer; }
   }
   #endregion
  
   #region Implementierung der Schnittstelle IName
   // Diese Schnittstelle verlangt keine Methoden.
   // Dafür hat sie aber Eigenschaften,
   // deren Implementierung wir garantiert haben.
 
   // Eigenschaft Nachname
   public string Nachname 
   {
     get { return m_Nachname; }
     set { m_Nachname = value; }
   } 
 
   // Eigenschaft Vorname
   public string Vorname 
   {
     get { return m_Vorname ; }
     set { m_Vorname  = value; }
   } 
 
   // Nur-Lese-Eigenschaft VollerName
   public string VollerName
   {
     get { return String.Concat(Vorname, " ", Nachname); } 
   }
   #endregion
}

Aufgrund der vertraglich garantierten Zusage, alle Anforderungen der Schnittstellen IName und IAdresse zu erfüllen, kann jeder, der Objekte der Klasse Mitarbeiter verwenden möchte, sicher sein, dass die in den Schnittstellen definierten Bedingungen eingehalten wurden.

Explizite ImplementierungBearbeiten

Stellen wir uns vor, zwei Schnittstellen erfordern Methoden, die zwar gleich heissen, aber nicht gleich funktionieren. Wenn man auf die Art und Weise wie wir es bisher gemacht haben, einfach die geforderte Funktion in seiner Klasse implementiert, kann es zu einer falschen Initialisierung kommen, wenn man nicht die Funktionalität beider Schnittstellen richtig implementiert. In diesem Fall gibt es die Möglichkeit der expliziten Implementierung der Schnittstellen:

public interface I1
{
  void Init();
}
public interface I2
{
  void Init();
}

// Explizite Implementierung der Schnittstellen
public class Klasse : I1, I2
{
  void I1.Init() {...} // Init aus Schnittstelle I1
  void I2.Init() {...} // Init aus Schnittstelle I2
}

In diesem Fall kann man dann nicht mehr über eine Klasseninstanz (also ein Objekt der Klasse) auf diese Schnittstellen zugreifen, sondern sie kann nur über die Schnittstelle aufgerufen werden:

Klasse obj = new Klasse();
I1 intfc1 = (I1)obj;
I2 intfc2 = (I2)obj;
intfc1.Init();
intfc2.Init();

PraxisbeispielBearbeiten

Siehe auchBearbeiten

...

WeblinksBearbeiten