29

I'm asking in case I'm missing something obvious, but I think I may have stumbled upon a bug in .NET's compiler.

I have two projects in a .NET solution, one visual basic, one C#.

C# code, consisting of three overloaded static methods with default values:

public static class Class1
{

    public static void TestFunc(int val1, int val2 = 0)
    {
    }

    public static void TestFunc(int val1 = 0)
    {
    }

    public static void TestFunc(string val1, int val2 = 0)
    { 
    }
}

Visual basic code, calling one of the overloaded methods:

Option Explicit On
Option Strict On
Imports ClassLibrary1

Module Module1
    Sub Main()
        Dim x As Integer
        Class1.TestFunc(x, 0)
    End Sub
End Module

Compiling this code will fail, saying:

'TestFunc' is ambiguous because multiple kinds of members with this name exist in class 'ClassLibrary1.Class1'.

Why would it see this method as ambiguous? There is only one Class1.TestFunc with an (int, int) signature. Is this a bug, or am I missing something?

w.brian
  • 16,296
  • 14
  • 69
  • 118
  • 2
    I would assume (though someone who knows VB.NET better should confirm/deny) that the `As Integer` isn't strong enough to dictate the type of the first argument. – dlev Oct 25 '11 at 18:10
  • @dlev - The code gets turned into MSIL, so it should be relatively interchangeable, right? I know there are some things that are not, but I don't think this is one. – Tim Oct 25 '11 at 18:13
  • Yeah, I'm thinking that perhaps VB has conversion rules that would allow you to pass an `Integer` to a C# function that takes a `string`. – Roman Starkov Oct 25 '11 at 18:13
  • I thought C# didn't have default arguments, is this new? – Matthew Oct 25 '11 at 18:14
  • @Matthew: Sorta, they were introduced in C# 4. – BoltClock Oct 25 '11 at 18:14
  • I deleted my answer because it was... wrong. I'm actually surprised the C# code compiled at all when I tried it. A call to `TestFunc(1)` calls `TestFunc( int val1 = 0 )`. It must be favoring a method without a default param, I will look at the spec when I have a chance. Anyway, I don't know, this is a good question. If it's some goofy VB thing then I won't have the answer. – Ed S. Oct 25 '11 at 18:14
  • 1
    On a side note though, I don't think it's a good idea to have two methods like that. Sure, the compiler seems ok with it, but if I were reading your code and saw `TestFunc( 10 )` I would not readily know which method was being called. – Ed S. Oct 25 '11 at 18:16
  • @Ed S. - The result is due to the fact that the integer is converted to a string, which VB allows. – JonH Oct 25 '11 at 18:16
  • Try casting `x` to an `Integer` and see if it works. Next step; some arcane article on the implicit conversions allowed in VB? – Ed S. Oct 25 '11 at 18:18
  • 2
    I think the only solution is to find a badger, a mushroom and a snake and spontaneously break out in song. – Ryan Ternier Oct 25 '11 at 18:22
  • 1
    @JonH: Yep, I figured as much. Yet another reason to dislike VB... – Ed S. Oct 25 '11 at 18:23
  • just for fun, try commenting out one function at a time and see which one causes trouble – Roman Oct 25 '11 at 18:25
  • 1
    @All the reason this is now such a large issue is the question edit was made to add `Option Strict On`. Without this key detail then the code would run and is ambiguious as the call to TestFunc is duplicated even though an integer is passed - due to implicit conversion. But now the question changes because @w.brian added `Option Strict On` to the code. – JonH Oct 25 '11 at 18:27

5 Answers5

23

Ultimately it appears to come down to how C# is implementing the optional parameters in your first static method. If you remove the default value on that, your solution should compile.

public static void TestFunc(int val1, int val2) 
{ 
} 

Actually, in this case, I'm somewhat surprised that the C# compiles. You should either use optional parameters in C# or overloads, but not both. For example, how would you disambiguate the following:

public static void TestFunc(int val1, int val2 = 0)     
{     
}     

public static void TestFunc(int val1)     
{     
}  

If I pass in the following, which of the two methods should be executed - the one with the optional parameter or the second without the second parameter?

TestFunc(1)

A better solution if you want to include optional parameters in your C# implementation would be to combine the first and second methods and check your default value as necessary:

public static void TestFunc(int val1, int val2 = 0)
{
}

public static void TestFunc(string val1, int val2 = 0)
{
}

Note, using this version, VB IS able to disambiguate which method to call.

Jim Wooley
  • 10,169
  • 1
  • 25
  • 43
  • 2
    +1 this was the right answer, just difficult for me to phrase it in such a good way :). – JonH Oct 25 '11 at 18:44
15

If you try to compile this in VB.NET you'll get

Sub TestFunc(ByVal val1 As Integer, Optional ByVal val2 As Integer = 0)

End Sub

Sub TestFunc(Optional ByVal val1 As Integer = 0)

End Sub

you'll get Public Sub TestFunc(val1 As Integer, [val2 As Integer = 0])' and 'Public Sub TestFunc([val1 As Integer = 0])' cannot overload each other because they differ only by optional parameters.

so I'll say that VB.NET is more limited than C# in optional parameters overload.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • This certainly provides the answer to the question. It seems less than ideal that something that is perfectly legal in C# would fail in VB in a less-than-obvious way, especially considering the compiler can determine with absolute certainty which method signature is being called. – w.brian Oct 25 '11 at 20:22
  • @ChrisDunaway C# can do it without problems. It has some rules. It prefers methods without extra arguments to methods with extra arguments. Read here http://msdn.microsoft.com/en-us/library/dd264739.aspx – xanatos Oct 26 '11 at 14:17
  • I don't get how the compiler _could_ possibly distinguish between the first and second methods in the original question for calls of the form `TestFunc(integer_value)`. – Kenny Evitt Oct 31 '11 at 14:04
2

Edit

When this question was first posted both:

Option Strict On

and

Option Explicit On

were not put into the question so this changes the answer dramitically.

Original answer before question edit

Because this:

 public static void TestFunc(int val1, int val2 = 0) 
    { 
    } 

Is ambiguious to:

public static void TestFunc(string val1, int val2 = 0) 
    {  
    } 

VB.net can convert an integer to a string.

JonH
  • 32,732
  • 12
  • 87
  • 145
  • 3
    Is it? val1 is string or int - enough to give it a separate signature. – TomTom Oct 25 '11 at 18:12
  • @TomTom its nto the same as C#. – JonH Oct 25 '11 at 18:14
  • This is not a "why", this is a regurgitation of what the compiler has already told us. – Ed S. Oct 25 '11 at 18:16
  • @EdS. If you pass an integer to a function expecting a string vb.net can convert that int to a string. – JonH Oct 25 '11 at 18:17
  • Option Strict is On, meaning VB will not implicitly convert anything. – w.brian Oct 25 '11 at 18:17
  • @w.brian When you say `option explicit on` that means the type of the variable and the type of cast should be specified. It has nothing to do with the implicit conversion. – JonH Oct 25 '11 at 18:18
  • 1
    I think @w.brian meant Option Strict On - that will turn off implicit conversions. – Tim Oct 25 '11 at 18:20
  • Correct, this problem does not have to do with implicit conversions, but differences in implementation of the optional parameters. – Jim Wooley Oct 25 '11 at 18:22
  • No, it didn't. If the single-argument method didn't exist, there wouldn't be a problem - having option strict off only does implicit conversions _if there is no exact match_. – Random832 Oct 25 '11 at 18:59
  • No, it _didn't_, because your belief about what would have been the problem if option strict were off was _incorrect_. That's like saying an overload with an int and a short is ambiguous because it can convert a short to an int - overload resolution just doesn't work that way, with or without option strict. – Random832 Oct 25 '11 at 19:06
  • @Random832 If I had a function that took a string and an int and another that took an int and an int (which the original question posed before the edit) and without option strict on, then you would recognize the issue. – JonH Oct 25 '11 at 19:16
  • I actually tested this before posting to make sure I was remembering correctly. Overload resolution doesn't become ambiguous just because an implicit conversion is available. Otherwise you'd never be able to call Console.WriteLine. – Random832 Oct 25 '11 at 19:24
  • @Random832 I dont think we are on the same page. – JonH Oct 25 '11 at 19:37
  • I'll make this simple: "If I had a function that took a string and an int and another that took an int and an int and without option strict on" - it would compile and run fine (if you call it with an int or a string, and not some type that can convert to both - though object works fine and selects the proper overload at runtime). You've not actually tried this; I have. You've apparently got a distorted view of how the type conversions in `option strict off` VB actually work. – Random832 Oct 25 '11 at 19:39
  • Option Strict Off Public Class Class1 Public Shared Sub Main() Dim x As Integer = 1 Dim y As String = "1" f(x) f(y) End Sub ' Define other methods and classes here Shared Sub f(ByVal a As Integer, Optional ByVal b As Integer = 0) Console.WriteLine("Called int method") End Sub Shared Sub f(ByVal a As String, Optional ByVal b As Integer = 0) Console.WriteLine("Called string method") End Sub End Class – Random832 Oct 25 '11 at 19:41
2

TestFunc is ambiguous because of default parameter. When TestFunc(x) is called, it is not sure if it is calling TestFunc with single parameter or TestFunc with default 2 parameters 2nd parameter being default one.

Birey
  • 1,764
  • 15
  • 20
  • 2
    He's not calling `TestFunc(x)`! He's calling `TestFunc(x, 0)`. – Roman Starkov Oct 25 '11 at 18:12
  • 1
    that call is fine, compiler did not like its other ambiguous functions. – Birey Oct 25 '11 at 18:14
  • I'm sorry but I think you're wrong. The ambiguity is between the two functions that both take two parameters. – Roman Starkov Oct 25 '11 at 18:14
  • @Bipins This is just not the case. There's no ambiguity here. Calling `TestFunc(1)` will just call the single-paramter version. – dlev Oct 25 '11 at 18:15
  • is it VB's problem to identify integer and string? – Birey Oct 25 '11 at 18:15
  • @romkyns: Haha, you're 100% right. I thought the C# code was failing to compile without the VB stuff there at all. I was wrong; the C# code *does* compile and work, so it's a problem in VB land. – Ed S. Oct 25 '11 at 18:17
  • does it need Option Explicit for VB to pass integer as integer and not to be confused with string even when it is explicitly declared as integer? – Birey Oct 25 '11 at 18:20
  • According to the top answer, `Option Strict` will fix this, not `Option Explicit`. Silly VB... – Roman Starkov Oct 25 '11 at 18:30
2

In the Visual Basic Language Specification 10 it states that.

A method with optional parameters is considered to have multiple signatures, one for each set of parameters that can be passed in by the caller. For example, the following method has three corresponding signatures:

 Sub F(x As Short, _
       Optional y As Integer = 10, _
       Optional z As Long = 20)

So your TestFunc(int val1, int val2 = 0) has two signatures in VB, which clashes with TestFunc(int val1) so TestFunc is ambiguous.

I can't find anything in the C# specification which says that optional parameters are treated as methods with multiple signatures. From the behaviour you are seeing I assume that in C# they are not considered to have multiple signatures, otherwise you would get a compiler error - which means that it is valid in C#. I assume C# will choose the method that gets called based on some rules as it can't call both for TestFunc(0).

Jim Wooley
  • 10,169
  • 1
  • 25
  • 43
Chris Diver
  • 19,362
  • 4
  • 47
  • 58
  • It's more the fact that the one-argument method can't be a candidate for this call in C# - VB rejects the method group outright (I'm not sure if it will even appear in intellisense) rather than just when called with a set of arguments that could resolve to more than one. EDIT it also appears that C# will prefer a method with fewer optional parameters - but if you have two methods with the same number you can still get an ambiguous error, but only if you actually call them. – Random832 Oct 25 '11 at 19:15