14

正在将一些代码从 VB.Net 转换为 C#,当我遇到这个时,在一些使用 Ionic Zip 库的代码中:

Dim zipEntry1 As ZipEntry = zipFile1.Entries(0)

很简单:

ZipEntry zipEntry1 = zipFile1.Entries[0];

我在 C# 上收到此错误:

无法使用 [] 将索引应用于“System.Collections.Generic.ICollection”类型的表达式

两者都使用相同版本的 DLL,两者zipFile1.Entries都是通用的ICollection.

我已经在 VB.Net 上测试了以下内容,并且成功构建:

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];
        }
    }
}

为什么会发生这种情况,有没有办法解决它?

4

4 回答 4

19

奇怪的是,看起来 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

为 Main 生成的 IL:

.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自己,如果你对这样做感到满意的话。

于 2013-04-07T16:55:17.190 回答
7

严格来说,它是无序ICollection<T>元素集合的接口(更准确地说,是一个元素不能通过索引单独访问的集合)。这只是根据定义

但是你仍然可以使用 LINQ 的ElementAt(int index)扩展方法。index每次调用它时,它都会遍历所有元素(所以它通常比较慢)。

注意:ICollection<T>不要与Collection<T>. 后者实现IList<T>(除其他外),根据定义确实指定每个元素都可以通过其索引访问。

于 2013-04-07T16:38:44.923 回答
0

VB 长期以来一直有为其类设置默认成员的想法,对于集合而言,默认成员始终是成员Item ()。

于 2013-04-07T20:48:58.687 回答
0

zipFile1.Entries(0)调用所谓的Default Query Indexer ,这是VB 语言规范中定义的鲜为人知的功能:

默认查询索引器

每个元素类型为 T 并且还没有默认属性的可查询集合类型都被认为具有以下一般形式的默认属性:

Public ReadOnly Default Property Item(index As Integer) As T
    Get
        Return Me.ElementAtOrDefault(index)
    End Get
End Property
于 2019-03-25T10:27:27.077 回答