Необязательные параметры в управляемых методах C++/CLI

Как я могу объявить управляемый метод в C++/CLI, который имеет необязательный параметр при использовании из C#?

Я украсил этот параметр как атрибутом Optional, так и атрибутом DefaultParameterValue (см.: Как кодируются значения параметров по умолчанию), но только атрибут Optional, по-видимому, учитывается.


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) директивы. Код IL, отображаемый Reflector, неверен.

Отражатель:

.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 ) 
    ...

3 ответа

Решение

Компилятор C# не использует атрибут [DefaultParameterValue] для установки значения по умолчанию, он использует директиву.param для получения значения, встроенного в метаданные. Едва ли документированный в спецификации CLI, только в Разделе II, глава 15.4.1, упоминается, что он может иметь значение FieldInit, 15.4.1.4 об этом ничего не говорит.

На этом все и заканчивается, компилятор C++/CLI не знает, как сгенерировать директиву. Вы не можете сделать эту работу.

Есть способ заставить это работать (обходной путь). Волшебное слово может быть обнуляемым, так как для обнуляемых типов по умолчанию всегда используется значение "ноль" (.HasValue == false)

Пример:

C++ CLI в заголовке:

String^ test([Optional] Nullable<bool> boolTest);

CLI C++ в.cpp файле:

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 и должен иметь тот же конечный результат, что и значение параметра по умолчанию.

Другие вопросы по тегам