Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

VS Project References

I have a library with an abstract (MustInherit) base class.  This base class is inherited by classes within the same library project, and classes in other library projects.  The base class has a procedure:

Protected MustOverride Sub SomeSub(Optional ByVal arg() As Object = Nothing)

Inheriting classes in the same library have no problems.  Inheriting classes in external libraries generate the following compile error:

error BC30307: 'Protected Overrides Sub SomeSub([arg() As Object = Nothing])' cannot override 'Protected Overridable MustOverride Sub SomeSub([arg() As Object = Nothing])' because they differ by the default values of optional parameters.

This error DISAPPEARS when:
a) arg is made mandatory (Optional removed)
b) arg is made a single object (not an array)
c) external libraries reference the base class library using a project reference instead of a file reference

I can’t switch to project references (option c), because this would interfere with our project versioning through our automated build.  And I’d prefer to not change the base class function (options a & b), because it is the best code solution and IT SHOULD WORK!!

Can someone explain to me why this is happening, or another way to work around it?  Is it just me, or are other people running into, ahem, “unusual” compile errors when using file references between projects?

Joe Paradise
Thursday, November 18, 2004

I've duplicated this problem twice now:  in a separate solution, and in a bare-bones class inserted into the original solution.  I'm betting it's a bug in VS.

I guess nobody else has seen this?  Or do you only use project references between projects?

Joe Paradise
Friday, November 19, 2004

Actually, I'm using C# which doesn't have optional arguments. :)

And I guess this is the problem here -- looks like you've just found a VB.NET bug related to the handling of optional arguments. As far as I know they're a VB feature, not a .NET feature (i.e. VB "simulates" them for you, there is no equivalent in the emitted MSIL code).

Chris Nahr
Saturday, November 20, 2004

Thanks for the info Chris.  I wasn't aware that Optional arguments are VB language specific.  That's the end of THAT keyword. 

Hate it when I find I'm using features that are leftovers from VB6, because they usually have problems like this.

Joe Paradise
Saturday, November 20, 2004

I'm not so sure if it is a bug in VB.NET.  Remember that one of the fundamentals of null (or Nothing) is that null != null. I would try two things to test it out. One, I know in c# you can cast null to an object type (ex: (Object)null) but I'm not sure if you can do that in VB.NET. Second, try setting the optional parameter to an empty object or something.

Finally, why not just create an overridden class that calls your other one? Something like:

Protected MustOverride Sub SomeSub(ByVal arg())

Protected Sub SomeSub()
  SomeSub(Nothing)
End Sub

Cory Foy (cornetdesign.com)
Saturday, November 20, 2004

"Remember that one of the fundamentals of null (or Nothing) is that null != null."

I don't know about "Nothing" in VB but for MSIL and C#, null is just a constant that is always identical with itself. As you said it can be cast to anything but the results are still identical.

Chris Nahr
Sunday, November 21, 2004

Joe, if you were using C# you could insert the following attribute in one file of each project to ensure cross-language compatibility:

[assembly: CLSCompliant(true)]

However, I just looked up the MSDN docs and found the following note:

"The current Microsoft Visual Basic compiler intentionally does not generate a CLS-compliance warning, however, a future release of the compiler will issue that warning."

So I'm afraid it won't help you right now. :(

But such tests should also be part of Microsoft's FxCop tool. Did you try running that on your projects?

Chris Nahr
Sunday, November 21, 2004

After some further digging I found that I was wrong about optional parameters. MSIL does indeed support them directly, but they're listed under "default values" (for method parameters) which is why I couldn't find it at first.

When calling a method with optional parameters from another language you have to pass the constant Type.Missing for any parameter you don't wish to specify. That's also the magic value that allows you to retrieve default parameter values by reflection.

Optional parameters are definitely not CLS compliant, though. "Variable length argument lists are not allowed" says MSDN, but you can use ParamArray to work around that.

Chris Nahr
Sunday, November 21, 2004

I definitely want to keep the code CLS compliant.  My gut instinct says this is the right thing to do.  Good point about FxCop.  That's one of those files sitting in my download directory that I've been meaning to start using.

Chris, check out  "Accessing Default Argument Values" in the VS help file.  It says that "The C# language does not support default arguments."  Maybe the help file is wrong?

ms-help://MS.VSCC.2003/MS.MSDNQTR.2003APR.1033/cpguide/html/cpconaccessingdefaultargumentvalues.htm

Cory's overloaded solution is what I'm going to go with.  Figured there was a way around this problem. 

Still bugs me that my original code works with project references, but fails with file references.

Thanks for everyone's replies.  You guys kick ass!

Joe Paradise
Monday, November 22, 2004

The page link got cut off, here's the missing part: cpconAccessingDefaultArgumentValues.htm

The page is correct in stating that C# does not support default arguments. You cannot declare methods with default arguments in C#, and you must use the Type.Missing workaround to call such methods from C#.

This workaround is detailed in knowledge base article KB305814, "HOW TO: Pass Optional Method Arguments from C#".

This also means that the C# section in the second example on the "Accessing Default Argument Values" page are incorrect and should be deleted.

While I was at it, I set up a sample solution with a VB and a C# project, and played around a bit with default arguments. It seems that Type.Missing cannot be converted to *anything*! It's a System.Object and cannot be cast to anything else, so any optional arguments must also be of type System.Object. I think that makes optional arguments de facto unusable from C#.

Chris Nahr
Tuesday, November 23, 2004

*  Recent Topics

*  Fog Creek Home