Ein Unterschied im Stil: IDictionary vs. Dictionary
Lesezeit: 7 Minuten
Ich habe einen Freund, der gerade erst in die .NET-Entwicklung einsteigt, nachdem er jahrelang in Java entwickelt hat, und nachdem ich mir einige seiner Codes angesehen habe, stelle ich fest, dass er ziemlich oft Folgendes tut:
IDictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();
Er deklariert das Wörterbuch als Schnittstelle und nicht als Klasse. Normalerweise würde ich folgendes tun:
Dictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();
Ich würde die IDictionary-Schnittstelle nur verwenden, wenn sie benötigt wird (z. B. um das Wörterbuch an eine Methode zu übergeben, die eine IDictionary-Schnittstelle akzeptiert).
Meine Frage ist: Hat seine Art, Dinge zu tun, irgendwelche Vorzüge? Ist dies eine gängige Praxis in Java?
Für lokale Variablen gibt es wirklich keinen Sinn.
Die beiden wichtigsten Implementierungen von IDictionary (Dictionary und ConcurrentDictionary) haben sehr unterschiedliche Leistungsmerkmale (z Count ist auf ersterem sehr schnell und auf letzterem sehr langsam). Daher würde ich empfehlen, die konkrete Klasse anzugeben (anstatt IDictionary) nach Möglichkeit in C#, damit der Verbraucher weiß, wie er das Wörterbuch auf performante Weise verwenden kann.
Wenn IDictionary ein “generischerer” Typ als Dictionary ist, ist es sinnvoll, den allgemeineren Typ beim Deklarieren von Variablen zu verwenden. Auf diese Weise müssen Sie sich nicht so sehr um die Implementierungsklasse kümmern, die der Variablen zugewiesen ist, und Sie können den Typ in Zukunft einfach ändern, ohne viel folgenden Code ändern zu müssen. Zum Beispiel wird es in Java oft als besser angesehen
Auf diese Weise bin ich sicher, dass der gesamte folgende Code die Liste als Liste und nicht als LinkedList behandelt, was es in Zukunft einfach macht, LinkedList für Vector oder jede andere Klasse, die List implementiert, auszutauschen. Ich würde sagen, das ist Java und guter Programmierung im Allgemeinen gemeinsam.
+1, aber vielleicht möchten Sie das Wort “allgemein” anstelle von “generisch” verwenden, das bereits eine Menge Bedeutung hat 🙂
– AakashM
20. Oktober 2009 um 15:49 Uhr
Diese Praxis ist nicht nur auf Java beschränkt.
Es wird auch häufig in .NET verwendet, wenn Sie die Instanz des Objekts von der verwendeten Klasse entkoppeln möchten. Wenn Sie die Schnittstelle statt der Klasse verwenden, können Sie den Unterstützungstyp jederzeit ändern, ohne den Rest Ihres Codes zu beschädigen.
Sie werden auch feststellen, dass diese Vorgehensweise häufig beim Umgang mit IoC-Containern und der Instanziierung mithilfe des Factory-Musters verwendet wird.
Aber Sie verlieren alle Funktionen, die Dictionary möglicherweise bietet und die nicht in IDictionary enthalten sind.
– Zügel
20. Oktober 2009 um 15:37 Uhr
Aber wenn Sie stattdessen IDictionary verwenden können … verwenden Sie die Funktionalität zunächst nicht.
– Justin Nießner
20. Oktober 2009 um 15:38 Uhr
Aber wenn Sie nur die Funktionalität in IDictionary benötigen … Es ist einfacher, IDictionary in Dictionary zu ändern als umgekehrt 🙂
– Svis
20. Oktober 2009 um 15:38 Uhr
Es ist einfacher, ein Wörterbuch in ein IDictionary umzuwandeln als umgekehrt. 🙂
– Zügel
20. Oktober 2009 um 15:40 Uhr
Der springende Punkt dieser Redewendung ist der Verlust der Funktionalität, die Dictionary bietet, IDictionary jedoch nicht. Wenn Sie Ihre lokale Variable als IDictionary deklarieren, müssen Sie Ihre Verwendung dieser Variablen auf das beschränken, was über die Schnittstelle verfügbar ist. Wenn Sie später feststellen, dass Sie eine andere Klasse verwenden müssen, die IDictionay anstelle von Dictionary implementiert, können Sie dies tun, indem Sie die eine Zeile ändern, die den konkreten Klassenkonstruktor aufruft (da Sie wissen, dass Ihr Code ihn nur als IDictionary verwendet, nicht als Dictionary ).
– Stephen C. Stahl
20. Oktober 2009 um 17:33 Uhr
Vitalij Liptchinsky
Ihr Freund folgt dem sehr nützlichen Prinzip:
“Abstrahieren Sie sich von Implementierungsdetails”
Aber wir beschäftigen uns mit Implementierungsdetails. Wir erstellen keine Schnittstelle zu einer Klasse – wir deklarieren einfach eine lokale Variable.
– Zügel
20. Oktober 2009 um 15:48 Uhr
Nicht wirklich, IMHO. Wenn Ihnen Interface-Mitglieder ausreichen, würde ich Ihnen wirklich empfehlen, bei Interface zu bleiben. Machen Sie Ihren Code so allgemein wie möglich.
– Vitaliy Liptchinsky
20. Oktober 2009 um 19:31 Uhr
Sie sollten immer versuchen, die Schnittstelle und nicht die konkrete Klasse zu programmieren.
In Java oder einer anderen objektorientierten Programmiersprache.
In der .NET-Welt ist es üblich, eine zu verwenden I um anzuzeigen, dass es sich um eine Schnittstelle handelt, die Sie verwenden. Ich denke, das ist häufiger, weil sie es in C# nicht haben implements und extends um auf die Vererbung von Klasse und Schnittstelle zu verweisen.
Ich denke, Whey würde tippen
class MyClass:ISome,Other,IMore
{
}
Und das merkt man ISome ein IMore sind Schnittstellen während Other ist eine Klasse
In Java ist so etwas nicht nötig
class MyClass extends Other implements Some, More {
}
Das Konzept gilt immer noch, Sie sollten versuchen, die Schnittstelle zu programmieren.
Für lokale Variablen und private Felder, die bereits Implementierungsdetails sind, ist es besser, konkrete Typen als Schnittstellen für die Deklarationen zu verwenden, da die konkreten Klassen einen Leistungsschub bieten (direkter Versand ist schneller als virtueller/Schnittstellenversand). Das JIT kann Methoden auch einfacher inline integrieren, wenn Sie in den lokalen Implementierungsdetails nicht unnötig in Schnittstellentypen umwandeln. Wenn eine Instanz eines konkreten Typs von einer Methode zurückgegeben wird, die eine Schnittstelle zurückgibt, erfolgt die Umwandlung automatisch.
Das ist eine Mikrooptimierung, die nur durchgeführt werden sollte, wenn Sie einen bestimmten Leistungsengpass lösen müssen, sie sollte nicht das Design vorantreiben.
– Yishai
20. Oktober 2009 um 18:40 Uhr
Es ist keine Mikrooptimierung. Sie befinden sich bereits in der Umsetzung, wo Sie sich für eine konkrete Umsetzung entschieden haben. Warum so tun, als hätten Sie es nicht auf Kosten der Leistung getan?
– Sam Harwell
21. Oktober 2009 um 13:41 Uhr
Travis Hesemann
Am häufigsten sehen Sie den Schnittstellentyp (IDictionary), der verwendet wird, wenn der Member für externen Code verfügbar gemacht wird, unabhängig davon, ob sich dieser außerhalb der Assembly oder direkt außerhalb der Klasse befindet. In der Regel verwenden die meisten Entwickler den konkreten Typ intern für eine Klassendefinition, während sie eine gekapselte Eigenschaft mithilfe des Schnittstellentyps verfügbar machen. Auf diese Weise können sie die Fähigkeiten des konkreten Typs nutzen, aber wenn sie den konkreten Typ ändern, muss sich die Schnittstelle der deklarierenden Klasse nicht ändern.
public class Widget
{
private Dictionary<string, string> map = new Dictionary<string, string>();
public IDictionary<string, string> Map
{
get { return map; }
}
}
später kann werden:
class SpecialMap<TKey, TValue> : IDictionary<TKey, TValue> { ... }
public class Widget
{
private SpecialMap<string, string> map = new SpecialMap<string, string>();
public IDictionary<string, string> Map
{
get { return map; }
}
}
ohne die Benutzeroberfläche des Widgets zu ändern und anderen Code, der es bereits verwendet, ändern zu müssen.
Das ist eine Mikrooptimierung, die nur durchgeführt werden sollte, wenn Sie einen bestimmten Leistungsengpass lösen müssen, sie sollte nicht das Design vorantreiben.
– Yishai
20. Oktober 2009 um 18:40 Uhr
Es ist keine Mikrooptimierung. Sie befinden sich bereits in der Umsetzung, wo Sie sich für eine konkrete Umsetzung entschieden haben. Warum so tun, als hätten Sie es nicht auf Kosten der Leistung getan?
– Sam Harwell
21. Oktober 2009 um 13:41 Uhr
Gergely Orosz
Soweit ich gesehen habe, verwenden Java-Entwickler häufiger Abstraktion (und Entwurfsmuster) als .NET-Entwickler. Dies scheint ein weiteres Beispiel dafür zu sein: Warum die konkrete Klasse deklarieren, wenn er im Wesentlichen nur mit den Schnittstellenmitgliedern arbeiten wird?
Ich war bis zum zweiten Satz bei dir. Wie sonst soll man sich einen Verweis auf einen Interface-Implementierer verschaffen? Sie müssen etwas instanziieren, das es implementiert. new IDictionary<string, double>() würde nicht funktionieren und macht sowieso keinen Sinn.
–Tom W
12. Juli 2011 um 17:32 Uhr
Ich verstehe nicht wirklich … mein Punkt war, dass jeder, der jemals das Wörterbuchmitglied verwendet, nur IDictionary-Funktionen verwenden möchte. Offensichtlich müssen Sie etwas instanziieren, aber wenn Sie dieses Muster verwenden, bedeuten die folgenden Codes dasselbe für jeden, der die Klasse verwendet: IDictionary dictionary = new Dictionary(); und IDictionary Wörterbuch = new ConcurrentDictionary(); Wenn Sie keine Dictionary- oder ConcreteDictionary-spezifischen Member verwenden, ist es sinnvoll, sie nicht als Typ der Klassenvariablen zu deklarieren und eine Abstraktionsebene hinzuzufügen.
– Gergely Orosz
13. Juli 2011 um 8:28 Uhr
12293000cookie-checkEin Unterschied im Stil: IDictionary vs. Dictionaryyes
Für lokale Variablen gibt es wirklich keinen Sinn.
– Joren
20. Oktober 2009 um 15:49 Uhr
Dupe: stackoverflow.com/questions/1484445/…
– mmx
20. Oktober 2009 um 17:33 Uhr
Eine gute Praxis ABER Achtung Leistungskosten: nimaara.com/2016/03/06/beware-of-the-idictionary-tkey-tvalue
– Majan
6. März 2016 um 18:39 Uhr
Die beiden wichtigsten Implementierungen von
IDictionary
(Dictionary
undConcurrentDictionary
) haben sehr unterschiedliche Leistungsmerkmale (zCount
ist auf ersterem sehr schnell und auf letzterem sehr langsam). Daher würde ich empfehlen, die konkrete Klasse anzugeben (anstattIDictionary
) nach Möglichkeit in C#, damit der Verbraucher weiß, wie er das Wörterbuch auf performante Weise verwenden kann.– Testamente
18. September 2017 um 7:25 Uhr
Zusätzlich,
IDictionary
hat einige Performance-‘Macken’ – nimaara.com/2016/03/06/beware-of-the-idictionary-tkey-tvalue .– Testamente
3. Januar 2019 um 11:11 Uhr