Forum: PC-Programmierung C# Interfaces/Schnittstellen


von Torben (Gast)


Lesenswert?

Hallo, ich glaube mich auf dem Holzweg zubefinden und etwas Hilfe wäre 
nicht verkehrt.

Kurz und knapp ist mein Verständnis der Interfaces in C#

"Interfaces sind ein Vertrag und alle Methoden müssen in der geerbten 
Klasse implementiert sein."

In meinem Beispiel möchte ich gerne verschiedene Herstellermultimeter in 
meiner Applikation über Intefaces nutzen. Der Hersteller bietet für die 
verschiedenen Multimeter je nach Preisklasse mehr Funktionen wie mit 
einem SimpleMultimeter und Complexmultimeter dargestellt. In dem Fall, 
dass es in der Zukunft ein MegaComplexmultimeter gibt, könnte ich die 
weiteren Funktionen des Gerätes über andere Interfaces nutzen, zumindest 
wäre das mein Verständnis.

Ist das zu kompliziert gedacht und ich sollte mir lieber Gedanken um das 
Protokoll und Hardwareinterface z.B. RS232/GPIB machen?

Kennt jemand für solch eine Problematik Bücher, Beispiele oder kann mich 
in die richtige Richtung schubsen, insbesondere, wenn auch noch der 
Hersteller variabel ist?
1
namespace InterfaceExample
2
{
3
    using System;
4
    interface IDcVoltage
5
    {
6
        float GetDcVoltage();
7
    }
8
9
    interface IAcVoltage
10
    {
11
        float GetAcVoltage();
12
    }
13
14
    interface IResistance
15
    {
16
        float GetResistance();
17
    }
18
19
    interface IFrequency
20
    {
21
        float GetFrequency();
22
    }
23
24
    class SimpleMultimeter : IAcVoltage ,IDcVoltage, IResistance
25
    {
26
        
27
28
        public float GetAcVoltage()
29
        {
30
            throw new NotImplementedException();
31
        }
32
33
        public float GetDcVoltage()
34
        {
35
            throw new NotImplementedException();
36
        }
37
38
        public float GetResistance()
39
        {
40
            throw new NotImplementedException();
41
        }
42
43
    }
44
45
    class ComplexMultimeter : IAcVoltage, IDcVoltage, IResistance, IFrequency
46
    {
47
        public float GetAcVoltage()
48
        {
49
            throw new NotImplementedException();
50
        }
51
52
        public float GetDcVoltage()
53
        {
54
            throw new NotImplementedException();
55
        }
56
57
        public float GetResistance()
58
        {
59
            throw new NotImplementedException();
60
        }
61
62
        public float GetFrequency()
63
        {
64
            throw new NotImplementedException();
65
        }
66
67
        
68
    }
69
70
    class Program
71
    {
72
        static void Main(string[] args)
73
        {
74
            var multimeter = new SimpleMultimeter();
75
            multimeter.GetAcVoltage();
76
            
77
            var multimeter2 = new ComplexMultimeter();
78
            multimeter2.GetFrequency();
79
80
        }
81
    }
82
}

von nicht"Gast" (Gast)


Lesenswert?

Moin,

so, wie du das hinschreibst, schreit es förmlich nach Komposition statt 
Vererbung^^.

mach dich mal schlau drüber. Wenn du Fragen hast, stell sie hier.


Grüße,

von Torben (Gast)


Lesenswert?

Danke für den Hinweis, jetzt komme ich immer mehr ins trudeln.

Jetzt müsste ich nochmal mein SW Konzept überdenken, deshalb würde ich 
kurz mein Konzept mit euch teilen.

Die MainApp (executable) bietet die GUI für den Endanwender. Der 
Endanwender kann in dieser Applikationen Plugins/Addons laden. Die 
Plugins sind Assemblies (DLL's), welche zur Laufzeit geladen werden in 
Anhängigkeit, welche Pluginlizenzen er besitzt und die Plugins/Addins 
sind z.b. für ein Multimeter. In der GUI besitzt der Endandwender 
eigentlich nur ein Multimeter, welche die Basisfunktionen zur Verfügung 
stellt und nahezu Typenunabhängig vom Hersteller funktioniert.

Wenn ich mir Beispiele für Interfaces anschaue findet man Beispiele für 
einen Logger sehr oft.
1
public interface Logger
2
    {
3
        public void Log(LogLevel level, string msg);
4
        public void Log(LogLevel level, string msgType, string msg);
5
        public void InitLogSession();
6
        public void EndLogSession();
7
        public void AddLogger(Logger chainedLogger);
8
        public void RemoveLogger(Logger chainedLogger);
9
    }

Wenn ich nun das Beispiel auf meine Projektanwende, dann sollte meiner 
Mainapplikation egalsein, welches Multimeter in dem Plugin angebunden 
ist.
Hauptsache ich bekomme über das Interface die Werte und mit dem 
Loggerbeispiel scheint es so zusein.

Mein Problem ist, dass neue Funktionen eines Multimeter immer zufolge 
hat, dass ich mein Interface anfassen muss und es ist zeitintensiv. Gibt 
es dafür ansonsten eine andere Möglichkeit?

von Peter II (Gast)


Lesenswert?

Torben schrieb:
> Mein Problem ist, dass neue Funktionen eines Multimeter immer zufolge
> hat, dass ich mein Interface anfassen muss und es ist zeitintensiv. Gibt
> es dafür ansonsten eine andere Möglichkeit?

bei C# kann man mit Reflexion die Methoden eines Objekts abfragen. Damit 
könnte man ein dynamischen Interface machen.

Aber was hilft es, wenn man Messgerät eine neue Funktion hat, aber die 
GUI damit nicht umgehen kann?

von Frank L. (Firma: Flk Consulting UG) (flk)


Lesenswert?

Hallo Torben,

Deine Grundgedanke ist schon soweit korrekt.

Du kannst relativ einfach prüfen, ob ein Messinstrument ein Interface 
implementiert.
1
var multimeter = new SimpleMultimeter();
2
var frequenceMeter = multimeter as IFrequency;
3
if (frequenceMeter  == null) // keine Frequenzmessung
4
{
5
}

Über solche Prüfungen kannst Du Deine GUI steuern und in Abhängigkeit 
prüfen ob ein Multimeter bestimmte Funktionen implementiert.

In Deinen Interfacen solltest Du jeweils den maximal möglichen 
Funktionsumfang einer Multimetertyps definieren. Dinge die ein 
Multimeter nicht unterstützt, liefern einen Konstantenwert oder einen 
Hinweis an der Oberfläche.

Gruß
Frank

von Torben (Gast)


Lesenswert?

@Peter II

>Aber was hilft es, wenn man Messgerät eine neue Funktion hat, aber die
>GUI damit nicht umgehen kann?

Natürlich könnte der Designer der UI schon Labels für dynamischen Text 
und Werte vorbereiten, aber das Databinding würde nicht funktionieren.

@Frank

Danke für die Bestätigung. Die Interfaces für die Maximalausstattung zu 
definieren wird natürlich extrem schwer, weil niemand in die Zukunft 
schauen kann.

Eine Frage würde ich gerne noch stellen. Das Beispiel wäre kein 
neutrales Interface, oder doch?
1
public interface Logger
2
    {
3
        public void Log(LogLevel level, string msg);
4
        public void Log(LogLevel level, string msgType, string msg);
5
        public void InitLogSession();
6
        public void EndLogSession();
7
        public void AddLogger(Logger chainedLogger);
8
        public void RemoveLogger(Logger chainedLogger);
9
    }

von nicht"Gast" (Gast)


Lesenswert?

Torben schrieb:
> Danke für die Bestätigung. Die Interfaces für die Maximalausstattung zu
> definieren wird natürlich extrem schwer, weil niemand in die Zukunft
> schauen kann.

Du kannst das aber dynamischer gestalten. Deine Klasse fürs Multimeter 
kann ja Informationen bereithalten, an denen sich dein UI entlang 
hangeln kann und gesagt bekommt, was sie mit den Daten anfangen soll. Am 
Ende des Tages ist es ja doch nur eine Zahl, die dargestellt werden 
will.

Torben schrieb:
> Eine Frage würde ich gerne noch stellen. Das Beispiel wäre kein
> neutrales Interface, oder doch?
> public interface Logger
>     {
>         public void Log(LogLevel level, string msg);
>         public void Log(LogLevel level, string msgType, string msg);
>         public void InitLogSession();
>         public void EndLogSession();
>         public void AddLogger(Logger chainedLogger);
>         public void RemoveLogger(Logger chainedLogger);
>     }

Das sieht für mich eher nach einer Klasse aus, die Vorgänge und Fehler 
in einem Programm mitloggen soll. So was in der Art wie: 
Netzwerkverbindung zu Remote Ziel aufgebaut". Also solcher Kram, der 
immer kommt, wenn man die verbosity von kommandozeilen Tools aufdreht.
In meinen Augen ist das für die Schnittstelle zu einem Multimeter 
ungeeignet.

Da würde ich eher so Sachen wie:
1
   public interface dataLogger{
2
        public enum getMeasurementType();
3
        public measurementRange getRange();
4
        public double currentValue();   
5
   }

erwarten.

von Frank L. (Firma: Flk Consulting UG) (flk)


Lesenswert?

Hallo Torben,

die Frage ist nicht so einfach zu beantworten.

Interface allgemein dienen nur einem Zweck, verschleiere was dahinter 
passiert.

Im Gegensatz zur Vererbung, könnten Objekte unterschiedlicher Art, die 
ein gemeinsames Interface implementieren in einer Liste vom Type des 
Interfaces gesammelt werden.

Ruft man dann eine Methode auf, die im Interface beschrieben ist, wird 
jedes Objekt seine Implementierung der Methode ausführen.

Zu Deiner Frage, ja jede Klasse, die dieses Interface implementiert, 
kann Daten loggen. Das können komplexe Dinge sein wie Log4Net oder Deine 
eigenen Objekte wenn sie dieses Interface implementieren.

Und ja, Du hast Recht, es gibt keine Möglichkeit im voraus alle 
Möglichkeiten zu bedenken. Du wirst irgendwann immer Deine GUI und Deine 
Klassen anpassen müssen.

Um diesen Zeitpunkt aber soweit wie möglich heraus zuschieben, ist es 
sinnvoll sich am maximalsten was heute geht zu orientieren und die 
Interface daran zu orientieren.

Die Interface bringen dir aber die Sicherheit, dass wenn Du eine 
Methodensignatur änderst oder eine Methode hinzufügst oder entfernst 
alle Klassen, die dieses Interface implementieren bzw. deren 
Abhängigkeiten beim Compilieren Fehler werfen. Du letztlich nur diese 
Bereiche anpassen musst.


Schau Dir in diesen Zusammenhang auch mal MEF an. MEF würde Deinen 
Gedanken einer Pluginstruktur optimal unterstützen.

Gruß
Frank

von Frank L. (Firma: Flk Consulting UG) (flk)


Lesenswert?

Hallo Torben,

hier mal ein Link zu einem guten MEF Tutorial:

http://www.just-about.net/mef-tutorial

Gruß
Frank

von jo (Gast)


Lesenswert?

var multimeter = new SimpleMultimeter();
var frequenceMeter = multimeter as IFrequency;
if (frequenceMeter  == null) // keine Frequenzmessung
{
}

das geht viel kürzer:

if (frequenceMeter  is IFrequency)

Gruß J

von Torben (Gast)


Lesenswert?

@Frank

Danke für den Hinweis zum MEF. MEF war mir bis eben nicht bekannt. Ich 
werde mir Tutorials anschauen und ausprobieren.

von Frank L. (Firma: Flk Consulting UG) (flk)


Lesenswert?

Hi Torben,

wenn Du Fragen zu MEF hast kannst Du mir auch gerne eine PM schicken.
Geht schneller und wir sparen uns blöde Kommentare.

Gruß
Frank

von valvestino (Gast)


Lesenswert?

jo schrieb:
> var multimeter = new SimpleMultimeter();
> var frequenceMeter = multimeter as IFrequency;
> if (frequenceMeter  == null) // keine Frequenzmessung
> {
> }
>
> das geht viel kürzer:
>
> if (frequenceMeter  is IFrequency)

I.d.R. will man mit einem Objekt vom Typ IFrequency auch was anfangen. 
Soll heißen, der "cast", den Frank außerhalb des ifs gemacht hat, musst 
Du noch im if machen. Von den Anzahl der Code-Zeilen ist beides in 
diesem Fall also gleichwertig. Davon abgesehen ist abr der as-Operator 
etwas effizienter: 
https://msdn.microsoft.com/de-de/library/cc488006.aspx

Grüße
Markus

von valvestino (Gast)


Lesenswert?

@Torben:
Deine Ausgangsfrage lese ich in der Art: Wie verwende ich Interfaces? 
Die wesentlich spannendere Frage ist aber: Warum verwende ich 
Interfaces? Die einfache Antwort auf diese Frage lautet: Um statische 
Abhängigkeiten von konkreten Implementierungen zu vermeiden.

Anders ausgedrückt: Durch Interfaces bist Du in der Lage verschiedene 
Implementierungen (Multimeter-Modell-A und Multimeter-Modell-B) auf die 
selbe Art und Weise über das abstrakte Interface zu verwenden (das beide 
implementieren), ohne dass es Dich interessieren muss, welches 
Multimeter-Modell denn nun konkret vorliegt.

Ein Interface zu definieren, ohne dass Objekte über dieses Interface 
verwendet werden, ist ziemlich sinnfrei und verkompliziert Deinen Code 
nur überflüssig.

Grüße
Markus

von Torben (Gast)


Lesenswert?

Hallo, soweit hatte ich die Interfaces auch verstanden, dass mir relativ 
egal wie das Multimeter angesteuert wird. Ich benötige in meiner 
Mainapplikation den PluginManager, GUI und Interfaces. Das Plugin muss 
sich um die Anbindung der einzelnen Multimeter kümmern. Indem Fall finde 
ich es nur doof, das jeder Pluginentwickler die Anbindung implementieren 
muss, obwohl es sicherlich vorkommt, das die Anbindung, Protokolle etc. 
für einige Multimeter identisch ist.

Ich bin noch etwas am überlegen, wie solche Fälle sinnvoll behandelt 
werden damit es nicht verschiedene Plugins für namentlich 
unterschiedliche Multimeter gibt, aber zur Ansteuerung identisch sind.

von valvestino (Gast)


Lesenswert?

Torben schrieb:
> Indem Fall finde
> ich es nur doof, das jeder Pluginentwickler die Anbindung implementieren
> muss, obwohl es sicherlich vorkommt, das die Anbindung, Protokolle etc.
> für einige Multimeter identisch ist.

Sowas wird oftmals als generisches Gerät implementiert.

von Frank L. (Firma: Flk Consulting UG) (flk)


Lesenswert?

Hallo Torben,

grundsätzlich schreit das nach einer eigenen Kommunikationsklasse.
Messgeräte, die die gleiche Art der Kommunikation verwenden, werden über 
die gleiche Klasse gehandelt.

Nochmal mein Tipp dazu beschäftige Dich mit MEF, hier findest Du alles 
was Du benötigst, um möglichst viele Dinge wiederzuverwenden und 
gleichzeitig modular zu bleiben.

Beschäftige Dich mal mit dem Thema "ImportingConstructor" innerhalb von 
MEF

Gruß
Frank

von Torben (Gast)


Lesenswert?

Ok danke für den weieteren Stickpunkt

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.