views:

743

answers:

3

Why does constructor ORDER matter in VB.Net? I am building a .Net type library which is meant to wrap an underlying COM library completely so that the consumers of the API can pretend to use a nice .Net library with .Net collections and whatnot instead of a COM library.

Currently most of my classes are just 1 to 1 wrappers built using Reflection and CodeDOM. These classes have an internal constructor which takes the underlying COM type as a parameter. The CodeDOM builds this as the first constructor to the class. Using these classes from C# proves to be no problem. All I need is a reference to the .Net library and all works good.

The problems appear when I try using these classes from a VB.Net project. If the first constructor has a COM type as an argument, the VB.Net project requires the COM interop assembly as a reference. If the first constructor has no arguments or has only managed types all works good. My class library is written in C#.

The following works:

public class ObjectID
{
    public ObjectID(int type, int id)
    {
        this.Type = type;
        this.ID = id;
    }

    internal ObjectID(COMLib.ObjectID id) : this(id.Type, id.ID) { }

    public int ID { get; set; }
    public int Type { get; set; }

    internal COMLib.ObjectID ToCOM()
    {
        COMLib.ObjectID id = new COMLib.ObjectID();
        id.ID = this.ID;
        id.Type = this.Type;
        return id;
    }
}

The following requires a reference to the COMLib.Interop assembly:

public class ObjectID
{
    internal ObjectID(COMLib.ObjectID id) : this(id.Type, id.ID) { }

    public ObjectID(int type, int id)
    {
        this.Type = type;
        this.ID = id;
    }

    public int ID { get; set; }
    public int Type { get; set; }

    internal COMLib.ObjectID ToCOM()
    {
        COMLib.ObjectID id = new COMLib.ObjectID();
        id.ID = this.ID;
        id.Type = this.Type;
        return id;
    }
}

Now I can solve this by creating a dummy private constructor to these classes as the first constructor but I'm more curious about what causes this? Why does the order of the constructor declarations matter in VB.Net?

Update

This sounded so insane that I started doubting it myself. Managed to replicate it with 3 projects.

C# Class library: Wrapped

namespace Wrapped
{
    public class Class1
    {
    }
}

C# Class library: Wrapper

namespace Wrapper
{
    public class Class1
    {
        internal Class1(Wrapped.Class1 c) { }
        public Class1() { }
    }
}

VB.Net Console app: Referer

Module Module1
    Sub Main()
        Dim w As New Wrapper.Class1
    End Sub
End Module

Wrapper refers to Wrapped Referer refers to Wrapper Referer does NOT refer to Wrapped

Dim w As New Wrapper.Class1

Produces an error

Reference required to assembly 'Wrapped, Version=1.0.0.0,
                                         Culture=neutral,
                                         PublicKeyToken=null'
containing the type 'Wrapped.Class1'.
Add one to your project.

Swapping the order of constructors takes care of the error.

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=442224

Update after playing around some more

Causes error:          No error:

     Vb-ReferTest          Vb-RefererTest
          |                      |                     Fixes error in Vb-
          V                      V                      RefererTest even
      Cs-Wrapper            Cs-Wrapper   Vb-Wrapper <-  if the Vb-Wrapper
          |                       \         /          and the RefererTest
          V                        V       V             have no direct
   Cs-WrappedLibrary           Cs-WrappedLibrary          relationship
+1  A: 

If correct (I can't easily verify), that is fasinating. The order shouldn't matter, AFAIK. If you are sure about it, then perhaps log as a bug on connect (with sample code).

Marc Gravell
Added some simple sample code after verifying myself again.
Mikko Rantanen
A: 

No, the order of your Constructors as they are listed in code (using either C# or VB.NET) does not matter.

However, in this specific case (I have not tested it) you may have found a bug. Before jumping to the "it breaks on my machine" conclusion, you may want to have someone else verify the issue on their machine, so you know it's not just your machine. I've had this happen before.

Chris Pietschmann
Also breaks on my work computer at the office. So tested on Vista 64bit and XP 32bit now. Was planning to test it tomorrow but since you mentioned, did it over RDesktop.
Mikko Rantanen
+1  A: 

Confirmed as a bug. Too difficult to fix for it to be worth it too, so I guess we'll just live with it. Fortunately it seems to require cross language projects with uncommon class setups to crop up.

Mikko Rantanen
Does the workaround provided as a comment in the Connect link work for you?
rwmnau
It does, yes. It's basically the same one as the last update on the question. If you control the source of the wrapper library an easier workaround is to move the no-parameter default constructor on top or add a private one if there isn't an existing no-parameter constructor as posted in the Workaround section on Connect. This fixes the problem for future users as well.
Mikko Rantanen