Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I specify Enumerable.Count() instead of List.Count?

When attempting to use the Enumerable.Count() extension method from Visual Basic, the following code results in a compile-time error:

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

The error produced is:

'Public ReadOnly Property Count As Integer' has no parameters and its return type cannot be indexed.

I'm targeting .NET 4.0, so extension methods should be supported. It's also worth noting that the equivalent code in C# infers the extension method correctly...

Why is the compiler unable to infer the use of Enumerable.Count, given the predicate I'm passing as an argument, and how can I use the extension method instead of the List's Count property?

like image 348
Dan J Avatar asked Apr 12 '13 00:04

Dan J


1 Answers

The VB.Net compiler first tries to look up Count on the List instance, and it finds the Count property. This property is used instead of the extension method since fields and properties always shadow extension methods by name. I don't know where this is stated in the Visual Basic Language spec, but you can read more in this MSDN Magazin article:

Fields and properties always shadow extension methods by name. Figure 4 shows an extension method and public field with the same name and various calls. Though the extension method contains a second argument, the field shadows the extension method by name and all calls using this name result in accessing the field. The various overloaded calls will all compile, but their results at run time may be unexpected since they will bind to the property and use the default property behavior to return a single character or result in a runtime exception. It is important to choose the names of your extension methods so you avoid clashes with properties, fields, and existing instance methods.

So, Count(Function(foo) foo.Bar = "a") could mean: call the Count-property with Function(foo) foo.Bar = "a", or take the result of the Count-property and index it with Function(foo) foo.Bar = "a", which could be totally valid, since indexed properties in VB.Net can take any parameter.

This works in C# (I guess) because it is easier for the C# compiler to distinguish between method calls and a property access, because unlike VB.Net C# does not allow arbitrary parameters on properties and indexed properties.


To use the extension method, you call it like you would call every other static (shared) method:

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

or call Call on IEnumerable explicitly:

Dim i As Integer = l.AsEnumerable().Count(Function(foo) foo.Bar = "a")
like image 177
sloth Avatar answered Oct 12 '22 18:10

sloth