如何指定Enumerable.Count()而不是List.Count?

时间:2013-04-12 00:48:11

标签: vb.net extension-methods

尝试使用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 个答案:

答案 0 :(得分:7)

VB.Net编译器首先尝试在Count实例上查找List,然后找到Count属性。由于字段和属性始终按名称影子扩展方法,因此使用此属性代替扩展方法。我不知道Visual Basic语言规范中的内容在哪里,但您可以在此MSDN Magazin article中阅读更多内容:

  

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

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

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


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

Function(foo) foo.Bar = "a"

或明确地Dim i As Integer = Enumerable.Count(l, Function(foo) foo.Bar = "a") 致电Call

IEnumerable

答案 1 :(得分:2)

我不确定为什么你没有把重载作为一个选项,但你应该能够将列表转换为IEnumerable(Of Foo),此时编译器将不再允许{{1} } property。

List(Of Foo).Count

答案 2 :(得分: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")

答案 3 :(得分:1)

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

VB允许您在名称后使用()访问属性,并且还允许您通过省略()来调用没有参数的函数。索引器也使用圆括号,而不是C#中的方括号。这些是用于简化编程的巨大VB功能的示例,实际上会导致更模糊,更难理解和容易出错的代码。

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

C#看到圆括号,并意识到你没有访问索引器,你必须调用一个函数,所以寻找一个函数。

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

public Action Count { get; set; }

啊......快乐的日子。

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