Error creating object instance when using overloaded constructor

I recently came across a strange problem that I could not explain, and I would be glad if anyone could clarify why this is happening.

The problem I encountered is as follows:

I have an interface that is implemented, for example:

namespace InterfaceTwo { public interface IA { } } namespace InterfaceTwo { public class A : IA { } } 

And one more interface, which is implemented in another project, for example:

 namespace InterfaceOne { public interface IB { } } namespace InterfaceOne { public class B : IB { } } 

I have an object that uses these interfaces in it constructors, for example:

 using InterfaceOne; using InterfaceTwo; namespace MainObject { public class TheMainObject { public TheMainObject(IA iaObj) { } public TheMainObject(IB iaObj) { } } } 

And finally, I have a class that aggregates the above object, for example:

 using InterfaceTwo; using MainObject; namespace ReferenceTest { public class ReferenceTest { public void DoSomething() { var a = new A(); var theMainObject = new TheMainObject(a); } } } 

Oddly enough, this code will not compile with the following error:

The type 'InterfaceOne.IB' is defined in an assembly that is not referenced.
You must add a reference to the assembly 'InterfaceOne, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null'.
c: \ users \ harry.baden \ documents \ visual studio 2013 \ Projects \ ReferenceTest \ ReferenceTest \ ReferenceTest.cs 11 13 ReferenceTest

I also found that if I changed one of the overloads to contain an additional parameter, it compiles ... Which made me think that the problem could be related to some kind of reflection problem that the compiler is working with.

Thanks,

Barak.

+5
source share
2 answers

See this one for an explanation of a similar problem that I ran into. To quote the answer from the link:

The C # standard states that overload resolution (section 7.5.3) is done by comparing each agreed signature to determine which one is best. He does not say what happens when the link is missing, so we must conclude that it is still necessary to compare these unpublished types.

It should be obvious in your example which overload you are using, but the compiler is not smart enough and still trying to compare both overloads, so both links are required.

Perhaps the easiest, but not the most pleasant solution (if you do not want to include the missing link, for which you may have a reason why not) is to add an additional parameter of the dummy effect, which makes it obvious to the compiler, which overloads you; or converting two TheMainObject constructors into two methods with different names, for example. TheMainObjectA(IA iaObj) and TheMainObjectB(IB ibObj) - that is, to avoid overloading altogether.

Another possible solution is to use the dynamic keyword (for .NET 4.0 and higher), although some people may discourage this, as this may lead to runtime errors if you are not careful:

 public class TheMainObject { public TheMainObject(dynamic obj) { if (obj is IA) { // work with IA ... } else if (obj is IB) { // work with IB ... } else { // exception ... } } } 

Thus, the compiler does not generate an error, since the obj parameter is evaluated at runtime - your source code will work. If you decide to use this solution, try also checking the RuntimeBinderException to avoid accidentally accessing invalid (non-existent) members of the dynamic type.

0
source

namespace dependency problem. The error message pretty mush said this: your TheMainObject is dependent on InterfaceOne and must be correctly specified

this is not directly related to constructor overload ...

Update: This is more of a compiler behavior. To determine which overloaded method to use, the compiler should

  • check all methods with the same name and the same # parameters to see if all parameter types are referenced
  • then select one method that matches the type of caller parameter (explicit or implicit).

We can verify that step 1 and step 2 are separated by the following code:

 using InterfaceOne; using InterfaceTwo; namespace MainObject { public class TheMainObject { public TheMainObject(IA obj) { } public TheMainObject(IB obj, int x) { } } } using InterfaceTwo; using MainObject; namespace ReferenceTest { public class ReferenceTest { public static void DoSomething() { var a = new A(); var theMainObject = new TheMainObject(a); //no error } } } 

The above code compiles because TheMainObject(IB obj, int x) not a candidate for new TheMainObject(a) . However, if the constructor is defined as

 public TheMainObject(IB obj) { } 

or

 public TheMainObject(IB obj, int x = 0) { } 

Link to InterfaceTwo.IB required.

You can avoid such link checking by calling the constructor at run time, but this is error prone and you have to be careful. For instance:

 public static void DoSomething() { var a = new A(); TheMainObject theMainObject = null; var ctor = typeof (TheMainObject).GetConstructor(new[] {typeof (IA)}); if (ctor != null) { theMainObject = (TheMainObject) ctor.Invoke(new object[] {a}); } } 

I did a bit more research and found the following resources. Basically, the type expansion / narrowing step should be aware of all the types involved. (The VB version is for reference only, because the C # specification is for VS.Net 2003).

C # overload resolution

Visual Basic Overload Resolution

+6
source

All Articles