10

尝试使用 Visual Basic 中的Enumerable.Count()扩展方法时,以下代码会导致编译时错误:

Imports System.Linq

Module Module1

    Sub Main()
        Dim l As New List(Of Foo) From {New Foo("a"), New Foo("b"), New Foo("a")}

        Dim i As Integer = l.Count(Function(foo) foo.Bar = "a")

        Console.WriteLine(i)
        Console.ReadLine()
    End Sub

    Class Foo

        Sub New(ByVal bar As String)
            Me.Bar = bar
        End Sub

        Public Property Bar As String
    End Class
End Module

产生的错误是:

'Public ReadOnly Property Count As Integer' 没有参数,它的返回类型不能被索引。

我的目标是 .NET 4.0,所以应该支持扩展方法。还值得注意的是,C# 中的等效代码正确地推断出扩展方法......

为什么编译器无法推断 Enumerable.Count 的使用,给定我作为参数传递的谓词,以及如何使用扩展方法而不是 List 的 Count 属性?

4

4 回答 4

7

CountVB.Net 编译器首先尝试查找List实例,然后找到Count属性。使用此属性而不是扩展方法,因为字段和属性总是按名称隐藏扩展方法。我不知道这在 Visual Basic 语言规范中的位置,但您可以在此MSDN 杂志文章中阅读更多内容:

字段和属性总是按名称隐藏扩展方法。图 4 显示了具有相同名称和各种调用的扩展方法和公共字段。尽管扩展方法包含第二个参数,但该字段会按名称隐藏扩展方法,并且使用此名称的所有调用都会访问该字段。各种重载调用都将编译,但它们在运行时的结果可能出乎意料,因为它们将绑定到属性并使用默认属性行为来返回单个字符或导致运行时异常。选择扩展方法的名称很重要,这样可以避免与属性、字段和现有实例方法发生冲突。

因此,Count(Function(foo) foo.Bar = "a")可能意味着:使用 调用Count-property Function(foo) foo.Bar = "a",或获取Count-property 的结果并使用 对其进行索引Function(foo) foo.Bar = "a",这可能是完全有效的,因为 VB.Net 中的索引属性可以采用任何参数。

这适用于 C#(我猜),因为 C# 编译器更容易区分方法调用和属性访问,因为与 VB.Net C# 不同的是,C# 不允许属性和索引属性上的任意参数。


要使用扩展方法,您可以像调用其他所有静态(共享)方法一样调用它:

Dim i As Integer = Enumerable.Count(l, Function(foo) foo.Bar = "a")

或明确Call调用:IEnumerable

Dim i As Integer = l.AsEnumerable().Count(Function(foo) foo.Bar = "a")
于 2013-10-04T14:59:29.737 回答
2

我不确定为什么您没有将重载作为选项,但是您应该能够将列表强制转换IEnumerable(Of Foo)为编译器将不再允许List(Of Foo).Count属性的点。

CType(l, IEnumerable(Of Foo)).Count(Function(foo) foo.Bar = "a")
于 2013-04-12T02:54:03.917 回答
2

要回答你关于为什么VB 在这种情况下不能做 C# 的问题......

VB 允许您使用()名称后访问属性,还允许您通过省略(). 索引器也使用圆括号,而不是 C# 中的方括号。这些是旨在使编程更容易的巨大 VB 功能的示例,这实际上会导致更模棱两可、更难理解和容易出错的代码。

因此,在这种特殊情况下,VB 看到您正在访问 Count,并假定它后面的括号是 Count 属性的索引器,而不是 Count 函数的参数。

C# 看到圆括号,并意识到您没有访问索引器,您必须调用函数,因此查找函数。

当然,在 C# 中也有歧义的空间。例如,与扩展方法同名的属性,它返回一个委托类型,将优先于扩展方法被调用......

public Action Count { get; set; }

啊……快乐的日子。

至于如何调用 IEnumerable.Count() 函数,强制转换(最好DirectCast())或直接执行扩展方法Enumerable.Count(...),远比创建一个全新的数组来调用 count 更可取......!

于 2013-11-11T18:36:44.067 回答
1

如果列表转换为数组,它可以工作

    Dim l As New List(Of Foo) From {New Foo("a"), New Foo("b"), New Foo("a")}
    Dim i As Integer = l.ToArray.Count(Function(x) x.Bar = "a")
于 2013-04-12T13:10:35.670 回答