2013-04-07 2 views
11

Были преобразование код из VB.Net в C#, когда я наткнулся на это, в каком-то коде с помощью Zip библиотеки Ionic:Почему я могу применить индексацию к ICollection в VB.Net, но не в C#

Dim zipEntry1 As ZipEntry = zipFile1.Entries(0) 

достаточно просто:

ZipEntry zipEntry1 = zipFile1.Entries[0]; 

Я получаю эту ошибку на C#:

Невозможно применить индексирование с [] к выражению ф ре «» System.Collections.Generic.ICollection

Оба используют ту же версию DLL, на обоих zipFile1.Entries является общим ICollection.

Я проверил ниже на VB.Net, и он строит successfullly:

Option Strict On 
Option Explicit On 

Imports Ionic.Zip 

Module Module1 

    Sub Main() 

     Dim zipFile1 = ZipFile.Read("C:\test") 
     Dim zipEntry = zipFile1.Entries(0) 

    End Sub 

End Module 

Это не строит:

using Ionic.Zip; 

namespace ConsoleApplication2 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var zipFile1 = ZipFile.Read(@"C:\test"); 
      var zipEntry = zipFile1.Entries[0]; 
     } 
    } 
} 

Почему это происходит, и есть ли способ вокруг него ?

ответ

16

Bizarrely достаточно, похоже, VB имеет специальную поддержку для IEnumerable<T> и неявно предоставляет индексатор которые фактически звонки Enumerable.ElementAtOrDefault. ICollection<T> extends IEnumerable<T>, поэтому там же средство существует. ICollection<T> не предоставляет «реального» индексатора, поэтому проблема при попытке использовать его с C#.

Пример программы:

Option Strict On 

Public Class Test 
    Public Shared Sub Main(args As String()) 
     Dim x as System.Collections.Generic.ICollection(Of String) = args 
     Console.WriteLine(x(0)) 
    End Sub 
End Class 

Сформирован IL для Main:

.method public static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = (01 00 00 00) 
    // Code size  15 (0xf) 
    .maxstack 2 
    .locals init 
     (class [mscorlib]System.Collections.Generic.IEnumerable`1<string> V_0) 
    IL_0000: ldarg.0 
    IL_0001: stloc.0 
    IL_0002: ldloc.0 
    IL_0003: ldc.i4.0 
    IL_0004: call  !!0 
    [System.Core]System.Linq.Enumerable::ElementAtOrDefault<string>(
     class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, 
     int32) 
    IL_0009: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_000e: ret 
} // end of method Test::Main 

Я нахожу очень странно, что VB предоставляет это неявно - это действительно опасно, чтобы сделать его выглядеть, как это чтобы индексировать в коллекцию, которая не обязательно обеспечивает эффективную операцию индексирования.

Конечно, вы можете позвонить самим ElementAtOrDefault, если вы довольны тем, что это делает.

+0

Я предполагаю, что идея о том, что вы * не можете * сделать это на C#, больше оставлена ​​от C, где вы утверждаете, что оператор '[]' может эффективно индексировать последовательность, используя режимы адресации x86. В то время как в VB вы говорите, что хотите третий элемент, и вы получаете 3-й элемент. – dialer

+1

@ dialer: Ну, положение индексатора не имеет * *, чтобы быть ужасно эффективным, но мне не нравится идея его неявного, без указания вызывающему, что это не обеспечивается объявлением класс. Бывают случаи, когда это может быть резко драматичным - представьте, если вы использовали цикл for по индексу вместо цикла foreach, для чего-то, что нужно было читать текстовый файл каждый раз, чтобы добраться до n-й строки (или что-то еще). –

7

Строго просматриваемый, ICollection<T> является интерфейсом к неупорядоченным коллекциям элементов (точнее, коллекция, элементы которой по отдельности не могут быть доступны по их индексу). Это всего лишь по определению.

Но вы все равно можете использовать метод расширения LINQ ElementAt(int index). Это будет просто перебирать все элементы index раз каждый раз, когда вы его вызываете (так, как правило, медленнее).

ПРИМЕЧАНИЕ: ICollection<T> не следует путать с Collection<T>. Последний реализует IList<T> (среди прочего), который по определению делает, указывая, что к каждому элементу можно получить доступ по его индексу.

+0

+1 для ответа на вопрос «есть ли способ обойти его», спасибо большое, все равно будет интересно узнать, почему. – JMK

+0

@JMK смотрите обновление. Именно так определяется интерфейс ICollection . – dialer

+2

@ dialer: Ваш ответ не объясняет, почему VB позволяет это (или что он делает). –

0

В.Б. уже давно был идея члена в по умолчанию для его классов, для коллекции всегда член Пункта().

+0

Это почти всегда соответствует индектору для класса, который вовсе не является вещью VB. Ярлык VB, продемонстрированный в этой теме для «ElementAtOrDefault», является странной спецификой VB. –

+0

За исключением этого случая, это не Item, а ElementAt –

Смежные вопросы