Why can I apply an indexer to an ICollection in VB.Net, but not in C#

前端 未结 4 1432
一整个雨季
一整个雨季 2021-01-17 10:23

Was converting some code from VB.Net to C#, when I came across this, in some code using the Ionic Zip library:

Dim zipEntry1 As ZipEntry = zipFile1.Entries(0         


        
相关标签:
4条回答
  • 2021-01-17 10:41

    VB has long had the idea of a default member for it's classes, which for collections is always the member Item().

    0 讨论(0)
  • 2021-01-17 10:48

    Bizarrely enough, it looks like VB has special support for IEnumerable<T> and implicitly provides an indexer which actually calls Enumerable.ElementAtOrDefault. ICollection<T> extends IEnumerable<T>, so the same facility exists there. ICollection<T> doesn't provide a "real" indexer, hence the problem when you try using it from C#.

    Sample program:

    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
    

    Generated IL for 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
    

    I find it very odd that VB provides this implicitly - it's really dangerous to make it look like it's fine to index into a collection which doesn't necessarily supply an efficient indexing operation.

    Of course, you can call ElementAtOrDefault yourself, if you're happy with what that does.

    0 讨论(0)
  • 2021-01-17 11:01

    Strictly viewed, ICollection<T> is an interface to an unordered collection of elements (more precisely, a collection whose elements cannot individually be accessed by their index). That is just by definition.

    But you can still use LINQ's ElementAt(int index) extension method. That would just iterate through all elements index times every time you call it (so it is generally slower).

    NOTE: ICollection<T> is not to be confused with Collection<T>. The latter implements IList<T> (among other things), which by definition does specify that each element can be accessed by its index.

    0 讨论(0)
  • 2021-01-17 11:04

    zipFile1.Entries(0) invokes the so-called Default Query Indexer, which is a little-known feature defined in the VB language specification:

    Default Query Indexer

    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
    
    0 讨论(0)
提交回复
热议问题