C#から使用するときにオプションのパラメーターを持つC++/CLIでマネージドメソッドを宣言するにはどうすればよいですか?
オプション 属性と DefaultParameterValue 属性の両方でパラメーターを装飾しました(参照: デフォルトのパラメーター値のエンコード方法 )が、オプションのみ属性は尊重されているようです。
C++/CLI:
_public ref class MyClass1
{
public:
MyClass1([System::Runtime::InteropServices::Optional]
[System::Runtime::InteropServices::DefaultParameterValue(2)]
int myParam1) ↑
{
System::Console::WriteLine(myParam1);
}
};
_
C#:
_var myInstance1 = new MyClass1(); // compiles and runs
_
出力:
_0
_
期待される出力:
_2
_
Visual C#IntelliSense:
_MyClass1.MyClass1([int myParam1 = 0]); // wrong default value
↑
_
編集:逆アセンブラを詳しく見ると、C++/CLIコンパイラが実際に必要な.param [1] = int32(2)
ディレクティブを生成していないことがわかります。 Reflectorによって表示されるILコードが間違っています。
リフレクター:
_.method public hidebysig specialname rtspecialname instance void .ctor([opt] int32 myParam1) cil managed
{
.param [1] = int32(2) // bug
...
_
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 )
...
_
C#コンパイラは、[DefaultParameterValue]属性を使用してデフォルト値を設定するのではなく、.paramディレクティブを使用してメタデータに埋め込まれた値を取得します。 CLI仕様にほとんど記載されていませんが、パーティションIIの15.4.1章でのみ、FieldInit値を持つことができると記載されており、15.4.1.4はそれについて沈黙しています。
ここで問題が発生し、C++/CLIコンパイラはディレクティブの生成方法を知りません。これを機能させることはできません。
これを機能させるための秘訣があります(回避策)。魔法の言葉はnull許容型であり、null許容型の場合、デフォルトは常に「null」です(.HasValue == false)
例:
ヘッダーのC++ CLI:
String^ test([Optional] Nullable<bool> boolTest);
.cppファイルのC++ CLI:
String^ YourClass::test(Nullable<bool> boolTest)
{
if (!boolTest.HasValue) { boolTest = true; }
return (boolTest ? gcnew String("True") : gcnew String("False"));
}
c#でテストするには:
MessageBox.Show(YourClass.test());
回避策として、コンストラクターをオーバーロードし、委任を使用することができます。これはJITによってインライン化され、デフォルトのパラメーター値と同じ最終結果になるはずです。