显式调用的 C# 构造函数歧义 - 错误 CS0012
C# Constructors Ambiguity with explicit call - Error CS0012
C# 中存在无法解释的歧义,我在其中明确尝试调用构造函数,但编译器认为它是不同的构造函数。我将从展示我们使用的简短 C# 架构开始。然后展示我创建的一个小的“工作”示例,以及对此的可能解决方案,但我仍然想了解为什么会发生这种情况。
架构:
- 连接 C++ 的 CLR DLL API。
- C#API使用桥接级别
- 使用 C# API.
的 C# 客户端应用程序
- 请注意,不允许 C# 客户端使用 CLR 级别。
我创建的例子
CLR DLL 中的 class:
#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 在 DLL 中包装 CLR 级别:
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;
}
}
请注意,有一个 internal
构造函数和一个 public
构造函数。
使用 C# API DLL 的可执行客户端:
using Outer;
namespace OneClient
{
class Program
{
static void Main(string[] args)
{
string myString = "Some String";
A testA = new A(myString);
}
}
}
故事转折:
在包裹CLR层级的DLL中,不是ALL API应该被外部客户端使用,而是可以被内部客户端使用,因此通过添加[=15=将内部暴露给内部客户端] 到包装 CLR 级别的 DLL 的 'AssemblyInfo.cs'。
问题
编译客户端代码时出现以下错误:
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'.
- 我无法使用
InnerOne
,因为不允许客户使用此级别。
- 客户端同时暴露给
A(string val)
和 A(AInner inner)
构造函数。
可能的解决方法:
- 删除
[assembly: InternalsVisibleTo("OneClient")]
- 由于特定客户端需要使用的其他 classes 内部结构,这是不可接受的。
- 将
A(string val)
构造函数更改为 A(string val, bool unique=true)
并使用它 A testA = new A(myString, true)
- 不是一个好的解决方案。
- 使用默认构造函数
A()
并调用 testA.Val = myString;
- 这实际上没问题,但代码太多。
- 将客户端代码从
A testA = new A(myString)
更改为 A testA = new A(val:myString);
- 这实际上是选择的解决方案。
问题
为什么会出现这种歧义?
- 我用
myString
调用 A(string val)
这实际上是一个 string
值
这很奇怪。
这是 Microsoft 编译器中的错误吗?
示例来源:
源代码 One.zip
我看不出这有什么问题,问题是我们习惯于以 wrong/briefly 的方式做事。
正确答案是:
A testA = new A(val:myString);
此外,您的所有调用(在这种方式下是对 constructor/initializer 的调用,但无论如何它都是一个调用)都应该带有参数名称。没有人(甚至我)写过它们,但是...
Why does this ambiguity happen?
因为为了满足构造函数重载决议,编译器需要知道所有的参数类型是什么,而不知道AInner
是什么。
为什么不将 AInner
版本公开为工厂方法:
static internal A Create(AInner inner)
{
return new A { _inner = inner };
}
C# 中存在无法解释的歧义,我在其中明确尝试调用构造函数,但编译器认为它是不同的构造函数。我将从展示我们使用的简短 C# 架构开始。然后展示我创建的一个小的“工作”示例,以及对此的可能解决方案,但我仍然想了解为什么会发生这种情况。
架构:
- 连接 C++ 的 CLR DLL API。
- C#API使用桥接级别
- 使用 C# API. 的 C# 客户端应用程序
- 请注意,不允许 C# 客户端使用 CLR 级别。
我创建的例子
CLR DLL 中的 class:
#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 在 DLL 中包装 CLR 级别:
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;
}
}
请注意,有一个 internal
构造函数和一个 public
构造函数。
使用 C# API DLL 的可执行客户端:
using Outer;
namespace OneClient
{
class Program
{
static void Main(string[] args)
{
string myString = "Some String";
A testA = new A(myString);
}
}
}
故事转折:
在包裹CLR层级的DLL中,不是ALL API应该被外部客户端使用,而是可以被内部客户端使用,因此通过添加[=15=将内部暴露给内部客户端] 到包装 CLR 级别的 DLL 的 'AssemblyInfo.cs'。
问题
编译客户端代码时出现以下错误:
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'.
- 我无法使用
InnerOne
,因为不允许客户使用此级别。 - 客户端同时暴露给
A(string val)
和A(AInner inner)
构造函数。
可能的解决方法:
- 删除
[assembly: InternalsVisibleTo("OneClient")]
- 由于特定客户端需要使用的其他 classes 内部结构,这是不可接受的。 - 将
A(string val)
构造函数更改为A(string val, bool unique=true)
并使用它A testA = new A(myString, true)
- 不是一个好的解决方案。 - 使用默认构造函数
A()
并调用testA.Val = myString;
- 这实际上没问题,但代码太多。 - 将客户端代码从
A testA = new A(myString)
更改为A testA = new A(val:myString);
- 这实际上是选择的解决方案。
问题
为什么会出现这种歧义?
- 我用
myString
调用A(string val)
这实际上是一个string
值 这很奇怪。
这是 Microsoft 编译器中的错误吗?
示例来源: 源代码 One.zip
我看不出这有什么问题,问题是我们习惯于以 wrong/briefly 的方式做事。
正确答案是:
A testA = new A(val:myString);
此外,您的所有调用(在这种方式下是对 constructor/initializer 的调用,但无论如何它都是一个调用)都应该带有参数名称。没有人(甚至我)写过它们,但是...
Why does this ambiguity happen?
因为为了满足构造函数重载决议,编译器需要知道所有的参数类型是什么,而不知道AInner
是什么。
为什么不将 AInner
版本公开为工厂方法:
static internal A Create(AInner inner)
{
return new A { _inner = inner };
}