There is an unexplained ambiguity in C#, where I explicitly try to call a constructor but the compiler thinks it is a different constructor. I will start with showing a short C# architecture we use. Then show a small "working" example I created, and the possible solution to this, but still I like to understand why this happens.
The Architecture:
- CLR DLL which bridges the C++ API.
- C# API which uses the bridge level.
- C# Client applications that use the C# API.
- Note that the C# Clients are not allowed to use the CLR level.
Example I created
A class in the CLR DLL:
#pragma once
#include <string>
using namespace System;
namespace Inner {
public ref class AInner
{
public:
AInner() : _data(new std::wstring(L"")) {}
~AInner() {
delete _data;
}
property String^ Val
{
String^ get()
{
return gcnew String((*_data).data());
}
void set(String^ value) {
System::IntPtr pVal = System::Runtime::InteropServices::Marshal::StringToHGlobalUni(value);
*_data = (const wchar_t*)pVal.ToPointer();
System::Runtime::InteropServices::Marshal::FreeHGlobal(pVal);
}
}
private:
std::wstring* _data;
};
}
Class wrapping the CLR level, in a DLL:
using System;
using Inner;
namespace Outer
{
public class A
{
public A()
{
_inner.Val = String.Empty;
}
public A(string val)
{
init(val);
}
public string Val
{
get
{
return _inner.Val;
}
set
{
_inner.Val = value;
}
}
internal A(AInner inner)
{
_inner = inner;
}
private void init(string Val)
{
_inner = new AInner();
_inner.Val = String.Empty;
}
private AInner _inner;
}
}
Note that there is an internal
constructor and a public
constructor.
Executable Client using the C# API DLL:
using Outer;
namespace OneClient
{
class Program
{
static void Main(string[] args)
{
string myString = "Some String";
A testA = new A(myString);
}
}
}
Twist in the story:
In the DLL wrapping the CLR level, not ALL API should be used by external clients, but can be used by internal clients, thus the internals are exposed to the internal clients by adding [assembly: InternalsVisibleTo("OneClient")]
to the 'AssemblyInfo.cs' of the DLL wrapping the CLR level.
The issue
When compiling the Client code I get the following error:
error CS0012: The type 'AInner' is defined in an assembly that is not referenced. You must add a reference to assembly 'InnerOne, Version=1.0.7600.28169, Culture=neutral, PublicKeyToken=null'.
- I cannot use
InnerOne
because clients are not allowed to use this level. - The client is exposed to both
A(string val)
andA(AInner inner)
constructors.
Possible Workarounds:
- Remove the
[assembly: InternalsVisibleTo("OneClient")]
- This is unacceptable due to other classes internals that the specific client needs to use. - Change the
A(string val)
constructor toA(string val, bool unique=true)
and use itA testA = new A(myString, true)
- Not a nice solution. - Use default constructor
A()
and calltestA.Val = myString;
- This is actually OK but to much code. - Change the client code from
A testA = new A(myString)
toA testA = new A(val:myString);
- This is actually the chosen solution.
Question
Why does this ambiguity happen?
- I call the
A(string val)
with themyString
which is actually astring
value This is very strange.
Is this a bug in Microsoft compiler?
Example Sources: Source Code One.zip