parametros parameter opcionales c# .net visual-studio visual-studio-2015 .net-4.6

opcionales - optional parameter in c#



¿Por qué los parámetros opcionales obtienen valores erróneos en Visual Studio 2015? (1)

Encontré un comportamiento raro en VS2015 Aquí están los detalles:

Tengo un proyecto .Net 4.6 haciendo referencia a un ensamblado 3.5. Este ensamblado define en una de sus interfaces el siguiente método que pude inspeccionar usando el descompilador Resharper.

void WriteString([MarshalAs(UnmanagedType.BStr), In] string data, [In] bool flushAndEND = true);

Tome nota del último argumento opcional flushAndEND que tiene un valor predeterminado de true . El problema ahora es cuando uso este método en mi proyecto, al pasar el ratón sobre el nombre del método se muestra la información general sobre herramientas VS que detalla la firma del método, excepto que para mí muestra un valor predeterminado incorrecto del argumento opcional flushAndEND . Aquí hay una captura de pantalla

Para empeorar las cosas, me he dado cuenta de que durante el tiempo de ejecución, al llamar al método WriteString solo con el primer parámetro, flushAndEND se establece en false y no su valor predeterminado definido en el archivo DLL al que me refiero. El impacto de esto en nuestro proyecto fue grande porque hizo que una gran característica de nuestra aplicación fuera inútil y bloqueó gran parte de nuestras pruebas de regresión.

Pude superar este problema al forzar el valor del argumento opcional como verdadero al invocar el método, pero me temo que hay otras llamadas en otro lugar del proyecto que sufren el mismo problema. Por lo tanto, necesitaré una mejor solución para esto o al menos para entender cuál es la causa de este comportamiento.

Acabamos de actualizar nuestro entorno hace semanas. Antes usábamos VS2013 y todo funcionó bien.

Soy consciente del error .Net 4.6 confirmado que causa que se pasen algunos valores erróneos a algunos argumentos y puedo relacionarlo con mi problema aquí, pero como se dice en el artículo, el error solo ocurre al compilar para la arquitectura x64. Mi proyecto es una aplicación de WPF y lo compilamos para que sea x32.

¿Por qué se llama a WriteString con un argumento predeterminado incorrecto?

Intentaré más adelante aislar el problema en un proyecto pequeño y ver si puedo reproducir el problema.

EDITAR: ¡He logrado aislar el problema y encontré algunas cosas interesantes!

Creé una aplicación de consola .NET 4.6 simple, agregué una referencia a mi Dll y escribí el siguiente código simple que consiste en enviar un comando a un dispositivo y leer la respuesta:

private static void Main(string[] args) { //Init managers ResourceManager ioMgr = new ResourceManagerClass(); FormattedIO488 instrument = new FormattedIO488Class(); //Connect to the USB device instrument.IO = (IMessage)ioMgr.Open("USB0::0x0957::0x0909::MY46312358::0::INSTR"); string cmd = "*IDN?"; //This is the problematic method from my dll instrument.WriteString(cmd); //Read the response string responseString = instrument.ReadString(); Console.WriteLine(responseString); Console.ReadKey(); }

Lo que hice a continuación, es abrir este proyecto desde VS 2013 y VS 2015. En ambas versiones de VS, reconstruí el proyecto y lo ejecuté. Aquí están los resultados:

VS2013: se ha llamado a WriteString utilizando el valor CORRECTO predeterminado de flushAndEND (que es true significa que se flushAndEND el búfer y finaliza el comando).

VS2015: WriteString ha llamado a WriteString utilizando el valor predeterminado INCORRECTO de flushAndEND que dio una excepción de tiempo de espera.

Inspecciones adicionales entre las dos versiones de Visual Studio muestran que el visor del navegador de objetos en VS2013 muestra la firma del método como:

void WriteString(string data, [bool flushAndEND = True])

mientras que el navegador de objetos en VS2015 muestra la firma del método como:

void WriteString(string data, [bool flushAndEND = False])

La única explicación de este comportamiento es que hay un problema con el compilador VS2015 que no lee los valores predeterminados correctos del ensamblado.


De acuerdo, encontré una forma de reproducir este error que cualquiera puede ver por sí mismo. Y, sobre todo, los programadores de Microsoft que trabajan en Roslyn y que necesitan arreglar esto. Hubo suficiente liderazgo en la pregunta de que este es un problema específico de las bibliotecas de interoperabilidad COM. Eso salió bien.

Busqué una biblioteca de tipos que está disponible ampliamente con un método que tiene un argumento bool con un valor predeterminado de verdadero . Hay exactamente uno, cuáles son las probabilidades :) Es el método SWbemQualifierSet.Add () , toma 3 argumentos booleanos que tienen un valor predeterminado de verdadero.

Primero generé la biblioteca de interoperabilidad ejecutando este comando desde el símbolo del sistema de Visual Studio:

tlbimp C:/Windows/SysWOW64/wbem/wbemdisp.tlb

Lo que produce una biblioteca de interoperabilidad WbemScripting.dll. Luego, escribió una pequeña aplicación de prueba que llama al método y agrega la biblioteca de interoperabilidad WbemScripting.dll como referencia:

class Program { static void Main(string[] args) { var obj = new WbemScripting.SWbemQualifierSet(); object val = null; obj.Add("foo", ref val); } }

Tenga en cuenta que en realidad no se ejecuta, solo estamos interesados ​​en el código que genera. Mirando el ensamblado con ildasm.exe:

IL_001e: ldstr "foo" IL_0023: ldloca.s val IL_0025: ldc.i4.1 IL_0026: ldc.i4.1 IL_0027: ldc.i4.1 IL_0028: ldc.i4.0 IL_0029: callvirt instance class WbemScripting.SWbemQualifier WbemScripting.ISWbemQualifierSet::Add(string, object&, bool, bool, bool, int32)

Sin problemas, los ldc.i4.1 pasan cierto . Tanto el Examinador de objetos como IntelliSense muestran correctamente verdadero como el predeterminado.

Luego ejecuté la versión más antigua de Tlbimp.exe que pude encontrar en mi máquina. Genera un ensamblado compatible con .NET 2.0.50727:

"C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Bin/TlbImp.exe" c:/windows/syswow64/wbem/wbemdisp.tlb

Reconstruye el proyecto de prueba, esta vez se ve así:

IL_001e: ldstr "foo" IL_0023: ldloca.s val IL_0025: ldc.i4.0 IL_0026: ldc.i4.0 IL_0027: ldc.i4.0 IL_0028: ldc.i4.0 IL_0029: callvirt instance class WbemScripting.SWbemQualifier WbemScripting.ISWbemQualifierSet::Add(string, object&, bool, bool, bool, int32)

Problema reproducido, observe cómo ldc.i4.0 ahora pasa falso . Su escenario exacto Todo lo demás se comporta como debería, tanto Object Browser como IntelliSense muestran false como deberían. Simplemente no coincide con el valor predeterminado que se especifica en la biblioteca de tipos COM.

Cada otra versión de Tlbimp.exe que tengo disponible, SDK versión 7.1 y hasta generar un buen código. Todos ellos generan ensamblados de .NET v4.0.

Caracterizar el error no es tan fácil. No veo un defecto obvio cuando descompilo la biblioteca de interoperabilidad "mala", muestra los valores predeterminados de las correcciones que se declaran:

.method public hidebysig newslot virtual instance class WbemScripting.SWbemQualifier marshal(interface) Add([in] string marshal(bstr) strName, [in] object& marshal(struct) varVal, [in][opt] bool bPropagatesToSubclass, [in][opt] bool bPropagatesToInstance, [in][opt] bool bIsOverridable, [in][opt] int32 iFlags) runtime managed internalcall { .custom instance void [mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32) = { int32(2) } .param [3] = bool(true) .param [4] = bool(true) .param [5] = bool(true) .param [6] = int32(0) .override WbemScripting.ISWbemQualifierSet::Add }

Por lo tanto, no es de extrañar que Resharper no esté de acuerdo con Object Browser e IntelliSense, seguramente se desensambla por sí mismo y no confía en las interfaces de metadatos .NET así que muestra verdadero como el predeterminado.

Por lo tanto, debo suponer que Roslyn es sensible a la versión de tiempo de ejecución de destino. En otras palabras, esto solo irá mal con las viejas bibliotecas de interoperabilidad COM que se crearon con herramientas anteriores a .NET 4.0. De lo contrario, no es extrañamente extraño, C # no comenzó a admitir argumentos predeterminados hasta la v4 y había formas incompatibles de especificar el valor predeterminado. El peor escenario es tener que usar un PIA que es provisto por un proveedor. Una circunstancia atenuante es que los valores predeterminados distintos de 0 / falso / nulo no son tan comunes. La forma más simple de ver una biblioteca problemática es mirando el ensamblado con ildasm.exe, haga doble clic en el manifiesto. Línea superior:

// Metadata version: v2.0.50727

Este es, sin duda, el comportamiento de ruptura de los proyectos existentes que se reconstruyen con VS2015, informe el error . Enlace a este Q + A para que no tenga que repetir todo.

La solución alternativa es simple, simplemente vuelva a crear la biblioteca de interoperabilidad con Tlbimp.exe como mostré. O elimine la biblioteca de interoperabilidad y agregue una referencia al componente COM para que la biblioteca de interoperabilidad se genere sobre la marcha al compilar. Si depende de un PIA de un proveedor, tendrá que pedirles una actualización o el procedimiento correcto para crear una nueva biblioteca de interoperabilidad.