Benutzer mit den meisten Antworten
Aufrufen generischer Ereiterungsmethoden

Frage
-
Hallo,
ich nutze eine Liste, die mit Elementen vom Typ double gefüllt ist. Welchen Unterschied macht es, wenn ich eine Erweiterungsmethode folgendermaßen aufrufe:
double temp = meineListe.First();
oder
double temp = meineList.First<double>();
Ich bin der Meinung, dass ich keinen Unterschied in der Funktion bekomme und wüsste gerne die Best Practice, oder eben doch die Unterschiede.
Gruß
Michael
- Bearbeitet Mike_Th Mittwoch, 8. September 2010 06:42 Rechtschreibung
Antworten
-
Hallo Michael,
es macht keinen Unterschied.
Der Compiler leitet den Typen anhand des Parameters, hier der Liste ab,
der für TSource in First ermittelt wird.
Komplizierter wird es erst, wenn mehrere Typparameter abgeleitet werden müssen.Allgemein beschreibt das: Generische Methoden (C#-Programmierhandbuch)
Willst Du es exakt wissen, so findest Du es in der C# Sprachspezifikation unter 7.5.2 Type Inference ff.
Wobei man leichter verständliche ergänzende Kost in Eric Lipperts Blog findet.Zur "Best Practice": Im allgemeinen läßt man es weg -
denn i. a. gilt: Mag der Compiler es ohne nicht, hilft es auch mit nicht.
Entwirft man eigene Methoden, sollten man darauf achten,
dass Mehrdeutigkeiten vermieden werden.Gruß Elmar
- Als Antwort markiert Mike_Th Mittwoch, 8. September 2010 17:25
-
Hallo Michael,
wenn "meineListe" typsicher definiert wurde (etwa: new List<double>...), wird das gleiche IL erzeugt.
Der Compiler setzt beide Aufrufe intern in die getypte Variante um.
Insofern ist es nur noch eine eher subjektive Sicht, ob einem <double> da besser gefällt.
Die meisten benutzten hier IMHO dann die Variante ohne <double> aus meiner "Erfahrung" - ich auch.
Grund u.a.: weniger zu schreiben, bei trotzdem klarer Typ-Erkennung. ggf. auch weniger Wartungsaufwand bei Änderung, also letztlich dem DRY Prinzip (Don't repeat yourself) folgend.
ciao Frank- Als Antwort markiert Mike_Th Mittwoch, 8. September 2010 07:44
-
Hallo Michael,
wenn Du Dir die Signatur von First genau anschaust, wird IEnumerable<TSource > als Parameter erwartet,
was wiederum u. a. von IList<T> implementiert wird, somit für List<T> uam. gilt.
Durch die Angabe von this beim ersten Parameter wird es (in C#) als Erweiterungsmethode gekennzeichnet
und kann so auf jede Instanz angewendet werden, die IEnumerable<T > implementiert.Der C# Compiler probiert jeweils alle implementierten Schnittstellen die Vererbungshierachie durch,
Gruß Elmar
und wählt die treffendste aus - was extrem vereinfacht ausgedrückt ist; in der C# Spezifikation
ist das ein höchst komplexes Regelwerk.- Als Antwort markiert Mike_Th Donnerstag, 9. September 2010 05:44
-
Hallo Michael,
> Wie funktioniert dass dann bei meinem meineListe.First()?
Du kannst First ja auch überschreiben. Hier ein Beispiel wie First implementiert sein könnte:using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; namespace WinFirst { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { List<double> liste = new List<double> { 1.0, 2.0 }; var f = liste.First(); } } } public static class MeineErweiterungen { ///<summary>[Beispiel] Gibt das erste Element einer Sequenz zurück. /// Typparameter: /// TSource: Der Typ der Elemente von source. ///</summary> ///<param name="source"> /// Das System.Collections.Generic.IEnumerable<T>, dessen /// erstes Element zurückgegeben werden soll. ///</param> ///<returns> /// Rückgabewerte: /// Das erste Element in der angegebenen Sequenz. ///</returns> public static TSource First<TSource>(this IEnumerable<TSource> source) { CheckNotNull(source, "source"); var list = source as IList<TSource>; // spezielle Beispiel-Optimierung if (list != null) return list.Count > 0 ? list[0] : default(TSource); using (var e = source.GetEnumerator()) return e.MoveNext() ? e.Current : default(TSource); } [DebuggerStepThrough] private static void CheckNotNull<T>(T value, string name) where T : class { if (value == null) throw new ArgumentNullException(name); } }
(wobei hier die Überladung: public static TSource First<TSource>(
this IEnumerable<TSource> source, Func<TSource, bool> predicate);
mal weggelassen ist. Insofern siehst Du dort, was Parameter in dieser Erweiterungsmethode sind. Anhand dieser entscheidet der Compiler die Wahl der Methode. Gerade mit den in C# 4.0 neu hinzugekommenen optionalen und benannten Parametern, und ebenfalss der erweiterten [Kovarianz und Kontravarianz (beachte, dass IEnumerable<T> jetzt kovariant ist, also kommt Variabilität hinzu] ist die Wahl der Signatur für den Compiler nicht immer klar und sogar in einigen Fällen unentscheidbar. Aber in diesem Fall (First) ist das für den Compiler ja einfach - wie ich ja bereits in meinem ersten Posting schrieb. Ich hoffe, meine Postings waren auch hilfreich ;-)
ciao Frank- Als Antwort markiert Mike_Th Donnerstag, 9. September 2010 05:44
Alle Antworten
-
Hallo Michael,
wenn "meineListe" typsicher definiert wurde (etwa: new List<double>...), wird das gleiche IL erzeugt.
Der Compiler setzt beide Aufrufe intern in die getypte Variante um.
Insofern ist es nur noch eine eher subjektive Sicht, ob einem <double> da besser gefällt.
Die meisten benutzten hier IMHO dann die Variante ohne <double> aus meiner "Erfahrung" - ich auch.
Grund u.a.: weniger zu schreiben, bei trotzdem klarer Typ-Erkennung. ggf. auch weniger Wartungsaufwand bei Änderung, also letztlich dem DRY Prinzip (Don't repeat yourself) folgend.
ciao Frank- Als Antwort markiert Mike_Th Mittwoch, 8. September 2010 07:44
-
Hallo Michael,
es macht keinen Unterschied.
Der Compiler leitet den Typen anhand des Parameters, hier der Liste ab,
der für TSource in First ermittelt wird.
Komplizierter wird es erst, wenn mehrere Typparameter abgeleitet werden müssen.Allgemein beschreibt das: Generische Methoden (C#-Programmierhandbuch)
Willst Du es exakt wissen, so findest Du es in der C# Sprachspezifikation unter 7.5.2 Type Inference ff.
Wobei man leichter verständliche ergänzende Kost in Eric Lipperts Blog findet.Zur "Best Practice": Im allgemeinen läßt man es weg -
denn i. a. gilt: Mag der Compiler es ohne nicht, hilft es auch mit nicht.
Entwirft man eigene Methoden, sollten man darauf achten,
dass Mehrdeutigkeiten vermieden werden.Gruß Elmar
- Als Antwort markiert Mike_Th Mittwoch, 8. September 2010 17:25
-
Allgemein beschreibt das: Generische Methoden (C#-Programmierhandbuch)
Hallo Elmar,
vielen Dank für die ausführlichen Infos. Die Ausführungen im ersten Link habe ich jetzt mal gelesen und mir ist folgendes aufgefallen:
Der Compiler kann Typparameter auf der Grundlage der übergebenen Methodenargumente ableiten. Eine Einschränkung oder ein Rückgabewert genügen ihm zur Ableitung des Typparameters nicht. Infolgedessen ist ein Typrückschluss bei Methoden ohne Parameter nicht möglich.
Wie funktioniert dass dann bei meinem meineListe.First()? Da habe ich auch keine Parameter. Du schriebst, der Parameter sei die Liste. Wie darf ich das deuten?
GrußMichael
- Bearbeitet Mike_Th Mittwoch, 8. September 2010 17:31 Ergänzung
-
Hallo Michael,
wenn Du Dir die Signatur von First genau anschaust, wird IEnumerable<TSource > als Parameter erwartet,
was wiederum u. a. von IList<T> implementiert wird, somit für List<T> uam. gilt.
Durch die Angabe von this beim ersten Parameter wird es (in C#) als Erweiterungsmethode gekennzeichnet
und kann so auf jede Instanz angewendet werden, die IEnumerable<T > implementiert.Der C# Compiler probiert jeweils alle implementierten Schnittstellen die Vererbungshierachie durch,
Gruß Elmar
und wählt die treffendste aus - was extrem vereinfacht ausgedrückt ist; in der C# Spezifikation
ist das ein höchst komplexes Regelwerk.- Als Antwort markiert Mike_Th Donnerstag, 9. September 2010 05:44
-
Hallo Michael,
> Wie funktioniert dass dann bei meinem meineListe.First()?
Du kannst First ja auch überschreiben. Hier ein Beispiel wie First implementiert sein könnte:using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; namespace WinFirst { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { List<double> liste = new List<double> { 1.0, 2.0 }; var f = liste.First(); } } } public static class MeineErweiterungen { ///<summary>[Beispiel] Gibt das erste Element einer Sequenz zurück. /// Typparameter: /// TSource: Der Typ der Elemente von source. ///</summary> ///<param name="source"> /// Das System.Collections.Generic.IEnumerable<T>, dessen /// erstes Element zurückgegeben werden soll. ///</param> ///<returns> /// Rückgabewerte: /// Das erste Element in der angegebenen Sequenz. ///</returns> public static TSource First<TSource>(this IEnumerable<TSource> source) { CheckNotNull(source, "source"); var list = source as IList<TSource>; // spezielle Beispiel-Optimierung if (list != null) return list.Count > 0 ? list[0] : default(TSource); using (var e = source.GetEnumerator()) return e.MoveNext() ? e.Current : default(TSource); } [DebuggerStepThrough] private static void CheckNotNull<T>(T value, string name) where T : class { if (value == null) throw new ArgumentNullException(name); } }
(wobei hier die Überladung: public static TSource First<TSource>(
this IEnumerable<TSource> source, Func<TSource, bool> predicate);
mal weggelassen ist. Insofern siehst Du dort, was Parameter in dieser Erweiterungsmethode sind. Anhand dieser entscheidet der Compiler die Wahl der Methode. Gerade mit den in C# 4.0 neu hinzugekommenen optionalen und benannten Parametern, und ebenfalss der erweiterten [Kovarianz und Kontravarianz (beachte, dass IEnumerable<T> jetzt kovariant ist, also kommt Variabilität hinzu] ist die Wahl der Signatur für den Compiler nicht immer klar und sogar in einigen Fällen unentscheidbar. Aber in diesem Fall (First) ist das für den Compiler ja einfach - wie ich ja bereits in meinem ersten Posting schrieb. Ich hoffe, meine Postings waren auch hilfreich ;-)
ciao Frank- Als Antwort markiert Mike_Th Donnerstag, 9. September 2010 05:44
-
Hallo Elmar,
jetzt wird einiges klarer. Die Signatur und wie man Erweiterungsmethoden deklariert hatte ich im MSDN schon gesehen. Nur war ich in der Funktionsweise verunsichert, weil ich IEnumerable<TSource> ja nicht wissentlich in den Klammern () übergebe, wie ich das bei anderen Methoden gewöhnt bin.
Was deinen zweiten Absatz angeht kann ich nur zustimmen. Das muss ich nicht alles verstehen ;-). Ich habe mich an Eric Lipperts Blog gehalten.
Gruß
Michael
-
Hallo Frank,
das hilft auch noch mal. Hatte mir Erweiterungsmethoden ja im MSDN schon angeschaut (s. Antwort an Elmar) und ich habe bei Eric gelesen wie umfangreich das ist, denn richtigen Typ abzuleiten.
>Ich hoffe, meine Postings waren auch hilfreich ;-)
Darüber brauchst du dir keine Sorgen machen. Auf Grund des Wissengefälles von oben nach unten, werden deine Postings bei meinen Fragen in der Regel hilfreich sein ;-).
Gruß
Michael
-
Hallo Michael,
die Regeln sind so komplex, das auch für ausgewiesene Experten wie Jon Skeet
überrraschendes zu Tage kommt: Dynamic type inference and surprising possibilitiesUnd ich werde niemals behaupten - weder heute noch in Zukunft -
die Kapitel in der C# Spezifikation 100% verinnerlicht zu haben ;-)Gruß Elmar