10

Consider the following minimal example:

Module Module1
    Private Enum MyEnum
        A
    End Enum

    Public Sub Main(args As String())
        AreEqual(CType(0, MyEnum), MyEnum.A)    ' Error here
    End Sub

    Private Function AreEqual(Of T)(item1 As T, item2 As T) As Boolean
        Return False
    End Function

    Private Function AreEqual(item1 As Object, item2 As Object) As Boolean
        Return False
    End Function
End Module

For some strange reason, overload resolution fails in the line marked with "Error here":

Error 6 Overload resolution failed because no accessible 'AreEqual' is most specific for these arguments:

Private Function AreEqual(item1 As Object, item2 As Object) As Boolean: Not most specific.

Private Function AreEqual(Of MyEnum)(item1 As MyEnum, item2 As MyEnum) As Boolean: Not most specific.

Why is the second function not "most specific"? Both CType(0, MyEnum) and MyEnum.A should be expressions that are statically typed as MyEnum.

Interestingly, I can only reproduce this problem with casting an Enum. AreEqual(CType(0, Int32), 0) and AreEqual(MyEnum.A, MyEnum.A) both compile without problems.

I know how to fix this. I know that I can just use AreEqual(Of MyEnum)(...). That is not the question. I'm curious why this happens. Some compiler bug? Interestingly, the corresponding C# code does work:

enum MyEnum { A, B }
static void Main(string[] args)
{
    AreEqual((MyEnum)0, MyEnum.A);
}

static bool AreEqual<T>(T item1, T item2) { return false; }
static bool AreEqual(object item1, object item2) { return false; }
Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • 1
    Just for the record: Yes, I know that `CType(0, Int32)` is a no-op, and yes, of course I use `Option Strict On`. And yes, I know that both of my functions don't do anything useful. It's just an [mcve](http://stackoverflow.com/help/mcve) designed to demonstrate a particular issue. – Heinzi Mar 05 '15 at 15:41
  • 2
    Hopefully the [language specification](http://www.microsoft.com/en-us/download/details.aspx?id=15039) allows someone to determine if this is a compiler bug or an unfortunate but correct side effect of the rules. Not me, though -- I thought the C# rules for inference and overloading were complex, but it's *nothing* compared to what VB has to offer. It's somewhat to be expected, given that it must account for dynamic typing as well. – Jeroen Mostert Mar 05 '15 at 16:11
  • Try using DirectCast instead (I'd try it but I'm mobile right now). I'm guessing that this might work. – Dave Doknjas Mar 05 '15 at 16:31
  • I call bug, the compiler is getting confused about 0 being implicitly convertible to any enum type. Note how it also fails with DirectCast(). Report this at connect.microsoft.com – Hans Passant Mar 05 '15 at 16:35
  • A late binding do work, as you can see in [this .NetFiddle](https://dotnetfiddle.net/vk349D), so I side with Hans on this one. – Bjørn-Roger Kringsjå Mar 05 '15 at 17:22
  • @HansPassant: Apparently, it works with Roslyn (tried it with Bjørn-Roger's fiddle link, which supports some Roslyn beta). Does it still make sense to report it? – Heinzi Mar 10 '15 at 16:08
  • 2
    It doesn't, Roslyn will compile in VSnext. – Hans Passant Mar 10 '15 at 16:15
  • 1
    I wonder if this original bug was in relation to this C# "quirk". http://stackoverflow.com/questions/14224465/compiler-value-type-resolution-and-hardcoded-0-integer-values (Be sure to read both the accepted and Lippert's answer.) – RLH Mar 12 '15 at 12:17

3 Answers3

1

It's related to integers, specifically zero. I think it's some not-quite-defined Option Strict artefact. I note default return type (in Intellisense) for CType() is object. Interestingly, doing any of these things removes the error:

  • Giving MyEnum any type, other than integer
  • AreEqual(0, MyEnum.A)
  • AreEqual(CType(1, MyEnum), MyEnum.A)

Yep. Crazy stuff, good find Heinzi!

Chalky
  • 1,624
  • 18
  • 20
0

I am admittedly way in over my head here, but the following doesn't produce any errors.

AreEqual(CType(CType(0, Int16), MyEnum), MyEnum.A) 

Where as

AreEqual(CType(CType(0, Integer), MyEnum), MyEnum.A) 

Does.

AreEqual(CType(1, MyEnum), MyEnum.A)

Also compiles fine.

Apologies if this is nonsense or useless.

GJKH
  • 1,715
  • 13
  • 30
0

I think your problem is that you are not restricting your generic type T. So it could also be an object.

i.e. if T is of type object then these are identical when translated to common intermediate language by the compiler:

 Private Function AreEqual(Of T)(item1 As T, item2 As T) As Boolean
    Return False
End Function

Private Function AreEqual(item1 As Object, item2 As Object) As Boolean
    Return False
End Function

I don't see why you need both. T will cater for object. So just drop the second one and everything should still work?

Daniel van Heerden
  • 836
  • 1
  • 7
  • 25