How can I declare a managed method in C++/CLI that has an optional parameter when used from C#?
如何在c++ /CLI中声明具有可选参数的托管方法?
I've decorated the parameter with both an Optional and a DefaultParameterValue attribute (see: How default parameter values are encoded), but only the Optional attribute seems to be honored.
我已经用可选属性和DefaultParameterValue属性(参见:默认参数值是如何编码的)对参数进行了修饰,但是似乎只对可选属性进行了修饰。
C++/CLI:
c++ / CLI:
public ref class MyClass1
{
public:
MyClass1([System::Runtime::InteropServices::Optional]
[System::Runtime::InteropServices::DefaultParameterValue(2)]
int myParam1) ↑
{
System::Console::WriteLine(myParam1);
}
};
C#:
c#:
var myInstance1 = new MyClass1(); // compiles and runs
Output:
输出:
0
Expected Output:
预期的输出:
2
Visual C# IntelliSense:
Visual c#智能感知:
MyClass1.MyClass1([int myParam1 = 0]); // wrong default value
↑
Edit: A closer look with a disassembler reveals that the C++/CLI compiler does indeed not generate the required .param [1] = int32(2)
directive. The IL code shown by Reflector is wrong.
编辑:用反汇编器仔细观察,就会发现c++ /CLI编译器确实不生成所需的.param [1] = int32(2)指令。Reflector显示的IL代码是错误的。
Reflector:
反射器:
.method public hidebysig specialname rtspecialname instance void .ctor([opt] int32 myParam1) cil managed
{
.param [1] = int32(2) // bug
...
ILDASM:
ILDASM:
.method public hidebysig specialname rtspecialname instance void .ctor([opt] int32 myParam1) cil managed
{
.param [1]
.custom instance void [System]System.Runtime.InteropServices.DefaultParameterValueAttribute::.ctor(object) = ( 01 00 08 02 00 00 00 00 00 )
...
3 个解决方案
#1
28
The C# compiler doesn't use the [DefaultParameterValue] attribute to set the default value, it uses the .param directive to get the value embedded in the metadata. Barely documented in the CLI spec btw, only Partition II, chapter 15.4.1 mentions that it can have a FieldInit value, 15.4.1.4 is silent about it.
c#编译器不使用[DefaultParameterValue]属性来设置默认值,而是使用.param指令来获取嵌入元数据中的值。在CLI spec中几乎没有记录,只有分区II,第15.4.1章提到它可以有FieldInit值,15.4.1.4对此保持沉默。
That's where the buck stops, the C++/CLI compiler doesn't know how to generate the directive. You cannot make this work.
这就是问题所在,c++ /CLI编译器不知道如何生成指令。你不能让它工作。
#2
8
There is a trick to make this working (workaround). the magic word is nullable, as for nullable types the default is always "null" (.HasValue == false)
有一个技巧可以让它工作(解决问题)。神奇的词是nullable,对于nullable类型,默认值总是“null”(。HasValue = = false)
Example:
例子:
C++ CLI in header:
c++ CLI头:
String^ test([Optional] Nullable<bool> boolTest);
C++ CLI in .cpp file:
cpp文件:
String^ YourClass::test(Nullable<bool> boolTest)
{
if (!boolTest.HasValue) { boolTest = true; }
return (boolTest ? gcnew String("True") : gcnew String("False"));
}
to Test it in C#:
用c#进行测试:
MessageBox.Show(YourClass.test());
#3
5
As a workaround, you can just overload the constructor, and use delegation. It will be inlined by the JIT and should end up with the same final result as a default parameter value.
作为解决方案,您可以重载构造函数,并使用委托。它将由JIT进行内联,最后得到与默认参数值相同的最终结果。
#1
28
The C# compiler doesn't use the [DefaultParameterValue] attribute to set the default value, it uses the .param directive to get the value embedded in the metadata. Barely documented in the CLI spec btw, only Partition II, chapter 15.4.1 mentions that it can have a FieldInit value, 15.4.1.4 is silent about it.
c#编译器不使用[DefaultParameterValue]属性来设置默认值,而是使用.param指令来获取嵌入元数据中的值。在CLI spec中几乎没有记录,只有分区II,第15.4.1章提到它可以有FieldInit值,15.4.1.4对此保持沉默。
That's where the buck stops, the C++/CLI compiler doesn't know how to generate the directive. You cannot make this work.
这就是问题所在,c++ /CLI编译器不知道如何生成指令。你不能让它工作。
#2
8
There is a trick to make this working (workaround). the magic word is nullable, as for nullable types the default is always "null" (.HasValue == false)
有一个技巧可以让它工作(解决问题)。神奇的词是nullable,对于nullable类型,默认值总是“null”(。HasValue = = false)
Example:
例子:
C++ CLI in header:
c++ CLI头:
String^ test([Optional] Nullable<bool> boolTest);
C++ CLI in .cpp file:
cpp文件:
String^ YourClass::test(Nullable<bool> boolTest)
{
if (!boolTest.HasValue) { boolTest = true; }
return (boolTest ? gcnew String("True") : gcnew String("False"));
}
to Test it in C#:
用c#进行测试:
MessageBox.Show(YourClass.test());
#3
5
As a workaround, you can just overload the constructor, and use delegation. It will be inlined by the JIT and should end up with the same final result as a default parameter value.
作为解决方案,您可以重载构造函数,并使用委托。它将由JIT进行内联,最后得到与默认参数值相同的最终结果。