Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visual Studio chooses wrong constructor when a library isn't referenced?

I am using Visual Studio 2010 with two projects.

One contains a project referencing Microsoft's Exchange.WebServices dll(ver1.2) for accessing ExchangeServices. I created a class which contains some helper methods and wrappers to carry out various tasks while connected to an Exchange server(through the ExchangeService API). The ExchangeService constructor can accept an enum of ExchangeVersion, to specify the server version info. So I created two constructors within my class.

public class ExchangeConnector(string ver)
{
    // Property assignments
}

public class ExchangeConnector(ExchangeVersion ver)
    :this(ver.toString()) //Using(or not using) "this", doesn't seem to matter...
{ }

I created the constructor which accepts a string parameter, so that other projects don't necessarily need to add the Exchange.WebServices library.

But then I ran into an un-foreseen issue.

When I create an instance of ExchangeConnector("Exchange2007_SP1") in my second project(that does not contain a reference to the Exchange.WebServices dll), Intellisense doesn't pick the right constructor and doesn't show any pre-compile errors. When I force the build, though, I get the following error:

Error: The type 'Microsoft.Exchange.WebServices.Data.ExchangeVersion' is defined
in an assembly that is not referenced. You must add a reference to assembly
'Microsoft.Exchange.WebServices, Version=14.0.0.0, Culture=neutral, 
PublicKeyToken=31bf3856ad364e35'.

I am not even using the constructor with the ExchangeVersion enum reference, but it requires me to have a reference to it?

If I comment out the constructor with the ExchangeVersion enum, everything compiles, works, no run-time errors. OR If I modify the overload constructor so Intellisense can't possibly confuse the two, such as:

public class ExchangeConnector(string url, ExchangeVersion ver)
{ 
  // Property assignments
}

When I call ExchangeConnector("Exchange2007_SP1"), the code compiles and works fine. No runtime errors.

It is almost as though VS can't resolve which constructor to properly use. Now I know I can add the reference to the second project and be done with it, but I am curious as to why VS is doing this. Any ideas?

like image 269
Scott Avatar asked Aug 09 '13 15:08

Scott


1 Answers

After inspiration from Scott(the other one) and the link provided by CodeCaster, I think I finally found my answer.

It as part of the C# Language Specification(Visual Studio .Net 2003 Edition).

In Section 10.10 Instance Constructors:

An instance constructor initializer of the form base(argument-listopt) causes an instance constructor from the direct base class to be invoked. That constructor is selected using argument-list and the overload resolution rules of Section 7.4.2. The set of candidate instance constructors consists of all accessible instance constructors contained in the direct base class (including any default constructor, as defined in Section 10.10.4). If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs.

Diving down further to the definition...

In Section 7.4.2 Overload resolution:

Once the candidate function members and the argument list have been identified, the selection of the best function member is the same in all cases:

  • Given the set of applicable candidate function members, the best function member in that set is located.
  • If the set contains only one function member, then that function member is the best function member.
  • Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in Section 7.4.2.2.
  • If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and a compile-time error occurs.

And even further...

Section 7.4.2.2 Better function member[3]

[3][http://msdn.microsoft.com/en-us/library/aa691338(v=vs.71).aspx]

Given an argument list A with a set of argument types {A1, A2, ..., AN} and two applicable function members MP and MQ with parameter types {P1, P2, ..., PN} and {Q1, Q2, ..., QN}, MP is defined to be a better function member than MQ if

  • for each argument, the implicit conversion from AX to PX is not worse than the implicit conversion from AX to QX, and
  • for at least one argument, the conversion from AX to PX is better than the conversion from AX to QX.

When performing this evaluation, if MP or MQ is applicable in its expanded form, then PX or QX refers to a parameter in the expanded form of the parameter list.

So in summary:

A constructor with the same number of arguments must be evaluated through implicit conversion as to which function member(constructor) would be better.

Hence why I needed to add the reference to the second project so the compiler could determine which constructor was better.

If the constructors don't have the same number of arguments, evaluation doesn't need to occur, and the compiler doesn't need the reference to the second project.

like image 136
Scott Avatar answered Sep 23 '22 14:09

Scott