Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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)

Simple enough:

ZipEntry zipEntry1 = zipFile1.Entries[0];

I get this error on C#:

Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.ICollection'

Both are using the same version of the DLL, on both zipFile1.Entries is a generic ICollection.

I have tested the below on VB.Net, and it builds 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

This does not build:

using Ionic.Zip;

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

Why does this happen, and is there a way around it?

like image 822
JMK Avatar asked Apr 07 '13 16:04

JMK


People also ask

What is the ICollection in C#?

The ICollection interface in C# defines the size, enumerators, and synchronization methods for all nongeneric collections. It is the base interface for classes in the System. Collections namespace.

Which interface does ICollection T inherit from?

The ICollection<T> interface is the base interface for classes in the System. Collections. Generic namespace. The ICollection<T> interface extends IEnumerable<T>; IDictionary<TKey,TValue> and IList<T> are more specialized interfaces that extend ICollection<T>.

Should I use collection or list c#?

The reason "why" you "should" use a Collection<T> instead of a List<T> is because if you expose a List<T> , then anyone who gets access to your object can modify the items in the list. Whereas Collection<T> is supposed to indicate that you are making your own "Add", "Remove", etc methods.

Why is ICollection used?

ICollection<T> is used because the IEnumerable<T> interface provides no way of adding items, removing items, or otherwise modifying the collection.


1 Answers

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.

like image 184
Jon Skeet Avatar answered Sep 29 '22 11:09

Jon Skeet