7

I haven't noticed this behaviour yet, maybe because i prefer query syntax in VB.NET and split the query and the execution-methods into different statements.

If i try to compile following simple query:

Dim wordList As List(Of String) = New List(Of String)
Dim longWords As Int32 = wordList.Count(Function(word) word.Length > 100)

The compiler doesn't like this because he expects no arguments for List.Count:

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

If i declare it as IEnumerable(Of String) it works as expected:

Dim wordSeq As IEnumerable(Of String) = New List(Of String)
Dim longWords As Int32 = wordSeq.Count(Function(word) word.Length > 100)

Why is that so? What prevents the compiler from using the Enumerable extension method Count instead of the ICollection.Count property. Note that i've added Imports System.Linq and both Option Strict and Option Infer are On. I'm using.NET 4.0 (Visual Studio 2010).

I'm confused because in C# this works without a problem:

List<String> wordList  = new List<String>();
int longWordCount = wordList.Count(word => word.Length > 100);
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Im not familiar with VB.NET but in C# a List has a Property `Count` which returns the length of the collection. You may be looking for the linq extention `Count` and getting the compiler "confused" ? – Alex Anderson Oct 07 '14 at 11:42
  • 1
    @AlexAnderson I suppose Tim's question comes down to: why is the compiler confused? The syntax makes it very clear you're looking for a method with one argument, not for a property - the VB.NET compiler should have no issue with his. – dcastro Oct 07 '14 at 11:45
  • @AlexAnderson: The title suggests that Tim knows what is going on, just not why. – Chris Oct 07 '14 at 11:45
  • 2
    I'd just like to add that, if this is an one-off, an alternative to declaring the variable as `IEnumerable` is using [`AsEnumerable`](http://msdn.microsoft.com/en-us/library/vstudio/bb335435(v=vs.100).aspx). E.g., `wordList.AsEnumerable().Count(...);` – dcastro Oct 07 '14 at 11:47
  • 1
    *in C# this works without a problem*: So use C# :p. Just kidding :) – Sriram Sakthivel Oct 07 '14 at 11:48

4 Answers4

6

It's by design, quote from MSDN:

The situation is simpler with properties: if an extension method has the same name as a property of the class it extends, the extension method is not visible and cannot be accessed.

Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
  • +1 So the VB.NET compiler is in this case(by design) not as smart as the C# compiler? – Tim Schmelter Oct 07 '14 at 11:48
  • @TimSchmelter Not as smart, or it's by-design? – DavidG Oct 07 '14 at 11:49
  • @TimSchmelter I'm trying to understand (from specs) where it comes from, I suppose it may be because of 11.8.1#6 but I'm not such sure... – Adriano Repetti Oct 07 '14 at 11:56
  • It is visible and can be accessed - you just need to cast the object first, or use 'AsEnumerable'. – Dave Doknjas Oct 07 '14 at 15:39
  • @TimSchmelter Keep in mind that it's a potentially ambigious situation, particularly so in VB. What if the property in question is invocable, for example. If `Count` returned a `Func` then what should the code do? – Servy Oct 07 '14 at 15:42
  • @DaveDoknjas yes, as Servy pointed out in his answer that's one of AsEnumerable() purposes. We were trying to point out the _reason_ for that behavior. – Adriano Repetti Oct 07 '14 at 15:44
  • @Servy well it _may_ do what C# does (detect this issue and stop compilation). Well I guess there is a reason for that (in VB design) but I still can't figure out which one (but maybe what Tim said about their priorities). – Adriano Repetti Oct 07 '14 at 16:02
  • 1
    @AdrianoRepetti You're forgetting the fundimental design goal of VB, to avoid failing at all costs and try to do *something* instead of failing, unlike C#'s design goal which is to inform the developer if there is an ambiguity so that they can clarify the intent. – Servy Oct 07 '14 at 16:04
  • @Servy :| yes, that's true. Automatic conversions between delegates should be a strong warning of that! – Adriano Repetti Oct 07 '14 at 16:05
1

In C#, only way to call a property is using property syntax (i.e) instance.PropertyName where as in Vb.Net there are two options.

Dim list As List(Of String) = New List(Of String)()
Dim count = list.Count() //Method 1
Dim count2 = list.Count  //Method 2

Assume you add a Count extension method with no parameters, what does list.Count() mean? It always gives priority to instance members so it points to Property.

Ok, What does list.Count(Function(s) s.StartsWith("a")) means? In this case it mean the extension method?

There is no consistency in the language then, I guess designers deliberately avoided this feature for consistency.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • They could have designed it in this way: if there is a property **and** an extension method, use the property. In the other case use either the property or the method. (+1 however since it might be one of the reasons) – Tim Schmelter Oct 07 '14 at 12:02
  • But there is always that case (an extension method conflicts with an instance method) and it's addressed by specs (extension method is ignored) but it's not _this_ case.. In that case I'd agree it should fail but in this I'm sure there is a reason but I can't _see_ it. – Adriano Repetti Oct 07 '14 at 12:04
  • Tim: yes, they could have done. But there is no consistency isn't it? I assume it is the matter of consistency in the language (Till someone comes with a fact we all can make only assumptions). – Sriram Sakthivel Oct 07 '14 at 12:06
  • 1
    @SriramSakthivel: I guess Eric Lippert's answer to this question would be "because it wasn't worth the cost of designing, implementing, testing and documenting it". I assume that the VB.NET compiler team also has to follow this rule. They gave a higher priority to the [power of LINQ's query syntax](http://stackoverflow.com/a/9039282/284240). – Tim Schmelter Oct 07 '14 at 12:11
  • 1
    Yeah, [Every feature is expensive](http://blogs.msdn.com/b/ericlippert/archive/2003/10/28/how-many-microsoft-employees-does-it-take-to-change-a-lightbulb.aspx). Even just 5 lines of code. – Sriram Sakthivel Oct 07 '14 at 12:33
1

If you cast to IEnumerable when you need the extension method, everything will be fine:

Dim longWords As Int32 = CType(wordList, IEnumerable(Of String)).Count(Function(word) word.Length > 100)

Or as dcastro mentioned in a comment:

Dim longWords As Int32 = wordList.AsEnumerable.Count(Function(word) word.Length > 100)
Dave Doknjas
  • 6,394
  • 1
  • 15
  • 28
  • Then i would really, really prefer a two-liner ;) – Tim Schmelter Oct 07 '14 at 15:40
  • Then I think the 'AsEnumerable' solution is better - it doesn't muddy up the statement too much. – Dave Doknjas Oct 07 '14 at 15:42
  • Yes, but i would always wonder for what it's good since `List(Of T)` already implements `IEnumerable(Of T)`. In VB.NET i like this more: `Dim longWords = From word In wordList Where word.Length > 100 Dim count = longWords.Count()`. It's more readable, more maintainable, better to debug and you can reuse the `longWords` variable. It's also as efficient. (+1 anyway) – Tim Schmelter Oct 07 '14 at 15:45
1

One of the primary purpose of the AsEnumerable method is specifically to be used when the type of an IEnumerable object has a member that takes precedence over one of the LINQ extension methods, so you should simply add in a call to AsEnumerable before calling Count, to remove the member conflict.

Servy
  • 202,030
  • 26
  • 332
  • 449