tags:

views:

353

answers:

2

I have a problem with the VB.NET compiler failing to compile a class (in a separate C# assembly) which contains two overloads of a method with generic arguments. The equivalent code in C# compiles against the same assembly with no errors.

Here are the two method signatures:

protected void SetValue<T>(T newValue, ref T oldValue)
protected void SetValue<T>(T? newValue, ref T? oldValue) where T : struct

Here is the code to three assemblies that demonstrate the problem. The first is the C# assembly with a Base class that implements the generic methods. The second is a C# class derived from Base and calls both overloads of SetValue correctly. The third is a VB class also derived from Base, but fails to compile with the following error message:

Error 1 Overload resolution failed because no accessible 'SetValue' is most specific for these arguments: 'Protected Sub SetValue(Of T As Structure)(newValue As System.Nullable(Of Integer), ByRef oldValue As System.Nullable(Of Integer))': Not most specific. 'Protected Sub SetValue(Of T)(newValue As System.Nullable(Of Integer), ByRef oldValue As System.Nullable(Of Integer))': Not most specific.

  1. Base Class assembly

    example:

    public class Base
    {
        protected void SetValue<T>(T newValue, ref T oldValue)
        {
        }               
        protected void SetValue<T>(T? newValue, ref T? oldValue) where T : struct
        {
        }
    }
    
  2. C# Derived Class

    example:

    public class DerivedCSharp : Base
    {
        private int _intValue;
        private int? _intNullableValue;    
        public void Test1(int value)
        {
            SetValue(value, ref _intValue);
        }        
        public void Test2(int? value)
        {
            SetValue(value, ref _intNullableValue);
        }
    }
    
  3. VB Derived Class

    example:

    Public Class DerivedVB
        Inherits Base    
        Private _intValue As Integer
        Private _intNullableValue As Nullable(Of Integer)    
        Public Sub Test1(ByVal value As Integer)
            SetValue(value, _intValue)
        End Sub    
        Public Sub Test2(ByVal value As Nullable(Of Integer))
            SetValue(value, _intNullableValue)
        End Sub
    End Class
    

Am I doing something wrong in the VB code, or are C# & VB different when it comes to generic overload resolution? If I make the method arguments in Base non-generic then everything compiles correctly, but then I have to implement SetValue for every type that I wish to support.

+2  A: 

It seems like you have to help the VB compiler a little to pick the right overload to call. The following compiles for me

Public Sub Test1(ByVal value As Integer)
    SetValue(Of Integer)(value, _intValue)
End Sub

Public Sub Test2(ByVal value As Nullable(Of Integer))
    SetValue(Of Integer)(value, _intNullableValue)
End Sub
Mattias S
This gets around the compiler error, but the call to SetValue inside Test2 resolves to the wrong overload. Good try though! I thought the problem was solved when it compiled correctly.
Mike Thompson
Sorry, my bad. I've now edited my reply so that Test1 calls SetValue(T, ref T) and Test2 calls SetValue(T?, ref T?).
Mattias S
+1  A: 

I stand by my comment that this is most likely a bug. However, I can reproduce it using the Mono VB compiler vbnc. The (somewhat mangled) error message here is “No non-narrowing (except object)” – which I presume refers to missing implicit conversions and hence to the same overload resolution problem.

The problem seems to be that the overload resolution of the compiler fails because of the ByRef arguments, since that requires some special management which is made explicit in the C# call but not in the VB call.

Konrad Rudolph