2011-02-11 29 views
27

¿Cómo puedo declarar un método administrado en C++/CLI que tiene un parámetro opcional cuando se usa desde C#?Parámetros opcionales en métodos administrados de C++/CLI

He decorado el parámetro con un atributo Optional y DefaultParameterValue (ver: How default parameter values are encoded), pero parece que solo se cumple el atributo Opcional.


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 

de salida:

0 

Resultados esperados:

2 

Visual C# IntelliSense:

MyClass1.MyClass1([int myParam1 = 0]); // wrong default value 
            ↑ 

Editar: Un vistazo más de cerca con un desensamblador revela que el compilador de C++/CLI no lo hace de hecho generar la directiva .param [1] = int32(2) requerida. El código IL mostrado por Reflector es incorrecto.

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

¿Hay una razón por la cual una sobrecarga no funcionaría, para sus propósitos? –

+0

@Wes P: razones estéticas. Haré sobrecargas cuando no puedo hacer que esto funcione, pero prefiero los parámetros opcionales si es posible. – dtb

Respuesta

27

El compilador de C# no utiliza el [DefaultParameterValue] atribuyen a establecer el valor predeterminado, se utiliza la directiva .PARAM para obtener el valor incrustado en los metadatos. Apenas documentado en la especificación CLI por cierto, solo la Partición II, capítulo 15.4.1 menciona que puede tener un valor FieldInit, 15.4.1.4 no dice nada al respecto.

Ahí es donde el dinero se detiene, el compilador C++/CLI no sabe cómo generar la directiva. No puedes hacer que esto funcione.

+3

Sí, error, ildasm.exe le muestra el verdadero negocio. Solo el atributo está presente. Hay cierta confuzzlement allí porque VB.NET usó el atributo, ha soportado argumentos opcionales durante mucho tiempo. Su código funcionará con VB.NET;) –

+1

'No puede hacer que esto funcione ...' - ¡desafío aceptado! Apuesto a que si usaras un evento de post-compilación que involucre algo como 'Mono.Cecil' que reescribiera un poco de metadatos, lo haría. Sin embargo, no estoy seguro si eso funcionará con los ensamblados de C++/CLR que contienen código no administrado. –

5

Como solución alternativa, puede sobrecargar el constructor y usar la delegación. Será indicado por el JIT y debería terminar con el mismo resultado final que un valor de parámetro predeterminado.

8

Hay un truco para que esto funcione (solución alternativa). la palabra mágica es anulable, como para los tipos anulables el valor predeterminado es siempre "nulo" (.HasValue == false)

Ejemplo:

C++ CLI en la cabecera:

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

C++ CLI en .cpp:

String^ YourClass::test(Nullable<bool> boolTest) 
{ 
    if (!boolTest.HasValue) { boolTest = true; } 
    return (boolTest ? gcnew String("True") : gcnew String("False")); 
} 

probarlo en C#:

MessageBox.Show(YourClass.test()); 
+0

¡Funciona! No se preocupe por los pasos posteriores a la compilación ni por ilmasm otros trucos de generación. :-) – TarmoPikaro

Cuestiones relacionadas