2016-04-29 4 views
10

Ich habe eine seltsame VB.NET-Sache bemerkt. Von this question kommend, habe ich eine Möglichkeit bereitgestellt, über Index auf Schlüssel und Werte der Wörterbücher 'KeysCollection und ValuesCollection zuzugreifen, um beispielsweise das erste Element zu erhalten. Ich weiß, dass es nur Sinn macht in einem SortedDictionary seit einem normalen Dictionaryis not ordered (na ja, Sie sollten sich nicht auf seine Reihenfolge verlassen).Warum kann ich auf ein Element in KeyCollection/ValueCollection per Index zugreifen, auch wenn IList (Of Key) nicht implementiert ist?

Hier ist ein einfaches Beispiel:

Dim sortedDict As New SortedDictionary(Of DateTime, String) 
sortedDict.Add(DateTime.Now, "Foo") 

Dim keys As SortedDictionary(Of DateTime, String).KeyCollection = sortedDict.Keys 
Dim values As SortedDictionary(Of DateTime, String).ValueCollection = sortedDict.Values 
Dim firstkey As DateTime = keys(0) 
Dim firstValue As String = values(0) 

Aber ich war überrascht, dass der Fragesteller sagte auf die Frage, dass es nicht kompiliert, während es kompiliert und ohne ein Problem für mich funktioniert:

System.Diagnostics.Debug.WriteLine("Key:{0} Value:{1}", firstkey, firstValue) ' Key:04/29/2016 10:15:23 Value:Foo 

So Warum kann ich es verwenden, als gäbe es einen Indexer, wenn es nicht tatsächlich einen in SortedDictionary(Of TKey, TValue).KeyCollection-class und auch keinen in der ValueCollection gibt. Beide implementieren , die die übergeordnete Schnittstelle von IList<T> ist. So können Sie es loopen und es hat eine Count Eigenschaft, aber Sie können nicht auf Elemente über Index zugreifen, wie ich oben tue.

Beachten Sie, dass es sich um eine neue Konsolenanwendung ohne Erweiterungen handelt. Ich kann auch nicht zur Definition des Indexers gehen (auch nicht mit Nachschärfer). Warum funktioniert es für mich?

Randbemerkung: es funktioniert nicht in C#. Ich erhalte den erwarteten Compiler-Fehler:

Cannot apply indexing with [] to an expression of type 'SortedDictionary.KeyCollection'

var dict = new SortedDictionary<DateTime, string>(); 
dict.Add(DateTime.Now, "Foo"); 
DateTime dt = dict.Keys[0]; // here 

Hier ein Screenshot des Kompilieren VB.NET-Code ist:

enter image description here

+1

Wenn Sie in VB.NET-Projekteinstellungen [System.Linq aus 'Importierte Namespaces' entfernen (http://i.stack.imgur.com/0y0mW.png), erhalten Sie den gleichen Compilerfehler wie in C#. Schlussfolgerung: VB.NET ruft GetEnumerator implizit auf. – GSerg

+0

@GSerg: seit wann? Ist das neu in VS 2015? Das ist eine sehr schlechte Entscheidung. Sie bemerken nicht einmal, dass Sie eine große Sequenz aufzählen, um einen bestimmten Index zu finden. Aber danke für den Hinweis, du hast Recht. Sie sollten das nur mit "Option Strict Off" (oder einem neuen Compiler-Hinweis) tun. –

+0

Dasselbe passiert in VS 2008. (Warum habe ich es übrigens zu 'GetEnumerator' geändert?Ich meinte 'ElementAt' und kann sie jetzt nicht mehr bearbeiten. Obwohl ich nicht sicher bin, was genau es heißt.) – GSerg

Antwort

7

Es ruft Enumerable.ElementAtOrDefault, nicht der Indexer.

// [10 13 - 10 31] 
IL_001f: ldloc.1  // keys 
IL_0020: ldc.i4.0  
IL_0021: call   !!0/*valuetype [mscorlib]System.DateTime*/ [System.Core]System.Linq.Enumerable::ElementAtOrDefault<valuetype [mscorlib]System.DateTime>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0/*valuetype [mscorlib]System.DateTime*/>, int32) 
IL_0026: stloc.2  // firstKey 

Dieses Verhalten wird in der Visual Basic Language Specification, 11.21.3 dokumentiert:

Every queryable collection type whose element type is T and does not already have a default property is considered to have a default property of the following general form:

Public ReadOnly Default Property Item(index As Integer) As T 
    Get 
     Return Me.ElementAtOrDefault(index) 
    End Get 
End Property 

The default property can only be referred to using the default property access syntax; the default property cannot be referred to by name. For example:

Dim customers As IEnumerable(Of Customer) = ... 
Dim customerThree = customers(2) 

' Error, no such property 
Dim customerFour = customers.Item(4) 

If the collection type does not have an ElementAtOrDefault member, a compile-time error will occur.

+0

Danke. Ist das irgendwo dokumentiert? –

+2

@TimSchmelter Anscheinend ist es: [Visual Basic Sprachspezifikation] (https://msdn.microsoft.com/en-us/library/ms234437.aspx), 11.21.3 (Kredite gehen an [Stephen Cleary] (http://stackoverflow.com/users/263693/stephen-cleary) unter https://social.msdn.microsoft.com/Forums/en-US/36492946-8560-4b03-8607-4a47a2b569d2/no-default-property-for- linked-list-in-csharp-wenn-es-für-vb-? forum = netfxbcl) – GSerg

0

Es gibt eine beträchtliche Leistungseinbußen, wenn wir Enumerable.ElementAtOrDefault oder Enumerable.ElementAt verwenden. Wenn die Quelle die IList (von T) -Schnittstelle nicht implementiert, hat Linq keine kürzere Route, um zu dem Element am angegebenen Index zu gelangen. Daher wiederholt es jedes Element, bis die Iterationszahl den Wert des angegebenen Index erreicht. Keine Magie. Aufzählbar. Count() hat die gleiche Geschichte, außer in diesem Fall ICollection-Schnittstelle, wenn durch Quelle implementiert, ergreift Linq es andernfalls Iteration, bis das letzte Element benötigt wird, um Count zu produzieren. Ich weiß nicht, warum Vb.net es implizit erlaubt, weil die Chancen stehen, dass Fälle wie diese unbemerkt bleiben, bis ich ernsthafte Leistungsprobleme habe. Das Dictionary implementiert nur ICollection nicht IList. Ich denke, man muss vorsichtig mit Vb.net sein, da es nicht so streng typisierte Sprache wie C# ist.

+1

Sie haben Recht, dieser automatisch implementierte magische Code ist eine schreckliche Quelle von Leistungsproblemen. In den meisten Fällen würden Sie es nicht bemerken. Aber wenn es Leistungsprobleme verursacht, ist es sehr schwierig, den Grund zu finden. Aber es hat nichts damit zu tun, dass VB.NET streng typisiert ist oder nicht. Sie erhalten diesen automatisch implementierten Indexer für Collection-Typen, selbst wenn Sie "Option Strict On" haben. Die einzige Möglichkeit, dies zu vermeiden, besteht darin, die Verwendung eines Indexers für Typen zu vermeiden, die "IList"/"IList (Of T)" nicht implementieren. Sie müssen also schon von diesem "Auto-Indexer" Notiz haben. –

+0

Nun, wenn dies der Fall ist, dann feuert es wirklich die Debatte warum C# über VB.net. –

+0

Während es eine potentielle Quelle von Leistungsproblemen ist, tritt es nur dann ein, wenn es keinen schnelleren Weg dafür gibt. [Hier] (http://stackoverflow.com/a/36918491/11683) @TimSchmelter hat 'foreach' verwendet, um den ersten Schlüssel zurückzugeben - der implizite' ElementAtOrDefault' würde genau das tun, aber ... implizit. Wenn Sie * etwas nach Index aus einer Sammlung abrufen müssen, die es nicht unterstützt, verwenden Sie am Ende "ElementAtOrDefault" oder schreiben eine konzeptionell identische Methode. VB macht das nur für dich, es ist irgendwie so. – GSerg

Verwandte Themen