¿Para qué se utiliza el tipo 'dinámico' en C# 4.0?

Resuelto Fahad asked hace 14 años • 10 respuestas

C# 4.0 introdujo un nuevo tipo llamado "dinámico". Todo suena bien, pero ¿para qué lo usaría un programador?

¿Existe alguna situación en la que pueda salvar el día?

Fahad avatar Apr 22 '10 19:04 Fahad
Aceptado

La dynamicpalabra clave se agregó, junto con muchas otras características nuevas de C# 4.0, para simplificar la comunicación con el código que reside en o proviene de otros tiempos de ejecución, que tiene diferentes API.

Tomemos un ejemplo.

Si tiene un objeto COM, como el Word.Applicationobjeto, y desea abrir un documento, el método para hacerlo viene con no menos de 15 parámetros, la mayoría de los cuales son opcionales.

Para llamar a este método, necesitarías algo como esto (estoy simplificando, este no es código real):

object missing = System.Reflection.Missing.Value;
object fileName = "C:\\test.docx";
object readOnly = true;
wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing);

¿Notas todos esos argumentos? Debe pasarlos ya que C# antes de la versión 4.0 no tenía una noción de argumentos opcionales. En C# 4.0, se ha hecho más fácil trabajar con las API COM mediante la introducción de:

  1. Argumentos opcionales
  2. Hacer refopcional para las API COM
  3. Argumentos nombrados

La nueva sintaxis para la llamada anterior sería:

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

¿Ves cuánto más fácil parece y cuánto más legible se vuelve?

Separemos eso:

                                    named argument, can skip the rest
                                                   |
                                                   v
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
                                 ^                         ^
                                 |                         |
                               notice no ref keyword, can pass
                               actual parameter values instead

La magia es que el compilador de C# ahora inyectará el código necesario y trabajará con nuevas clases en el tiempo de ejecución, para hacer casi exactamente lo mismo que hacía antes, pero la sintaxis se le ha ocultado, ahora puede concentrarse en el qué , y no tanto sobre el cómo . A Anders Hejlsberg le gusta decir que tienes que invocar diferentes "encantamientos", lo cual es una especie de juego de palabras con la magia de todo el asunto, donde normalmente tienes que agitar tu(s) mano(s) y decir algunas palabras mágicas en el orden correcto. para poner en marcha cierto tipo de hechizo. La antigua forma API de hablar con objetos COM era mucho de eso, necesitabas pasar por muchos obstáculos para convencer al compilador de que compilara el código por ti.

Las cosas se estropean aún más en C# antes de la versión 4.0 si intentas hablar con un objeto COM para el que no tienes una interfaz o clase, todo lo que tienes es una IDispatchreferencia.

Si no sabes qué es, IDispatches básicamente una reflexión para objetos COM. Con una IDispatchinterfaz, puede preguntarle al objeto "¿cuál es el número de identificación del método conocido como Guardar?", crear matrices de un cierto tipo que contengan los valores de los argumentos y, finalmente, llamar a un Invokemétodo en la IDispatchinterfaz para llamar al método, pasando todos la información que habéis conseguido reunir juntos.

El método Guardar anterior podría verse así (definitivamente este no es el código correcto):

string[] methodNames = new[] { "Open" };
Guid IID = ...
int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid);
SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... });
wordApplication.Invoke(methodId, ... args, ...);

Todo esto por solo abrir un documento.

VB tenía argumentos opcionales y soporte para la mayor parte de esto hace mucho tiempo, por lo que este código C#:

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

Básicamente es simplemente C# ponerse al día con VB en términos de expresividad, pero hacerlo de la manera correcta, haciéndolo extensible, y no solo para COM. Por supuesto, esto también está disponible para VB.NET o cualquier otro lenguaje creado sobre el tiempo de ejecución .NET.

Puede encontrar más información sobre la IDispatchinterfaz en Wikipedia: IDispatch si desea leer más al respecto. Es algo realmente sangriento.

Sin embargo, ¿qué pasaría si quisieras hablar con un objeto de Python? Hay una API diferente para eso que la que se usa para los objetos COM, y dado que los objetos Python también son de naturaleza dinámica, es necesario recurrir a la magia de reflexión para encontrar los métodos correctos para llamar, sus parámetros, etc., pero no el .NET. reflexión, algo escrito para Python, muy parecido al código IDispatch anterior, pero completamente diferente.

¿Y para Rubí? Una API diferente todavía.

¿JavaScript? Mismo trato, API diferente para eso también.

La palabra clave dinámica consta de dos cosas:

  1. La nueva palabra clave en C#,dynamic
  2. Un conjunto de clases de tiempo de ejecución que sabe cómo tratar con los diferentes tipos de objetos, que implementan una API específica que dynamicrequiere la palabra clave y asigna las llamadas a la forma correcta de hacer las cosas. La API incluso está documentada, por lo que si tiene objetos que provienen de un tiempo de ejecución que no están cubiertos, puede agregarlos.

Sin embargo, la dynamicpalabra clave no pretende reemplazar ningún código existente únicamente para .NET. Claro, puedes hacerlo , pero no se agregó por esa razón, y los autores del lenguaje de programación C#, con Anders Hejlsberg al frente, han sido muy inflexibles en que todavía consideran a C# como un lenguaje fuertemente tipado y no sacrificarán ese principio.

Esto significa que aunque puedes escribir código como este:

dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;

y hacer que se compile, no estaba pensado como una especie de sistema mágico que permite descubrir lo que quiso decir en tiempo de ejecución.

El objetivo era hacer que fuera más fácil hablar con otros tipos de objetos.

Hay mucho material en Internet sobre la palabra clave, sus defensores, oponentes, discusiones, peroratas, elogios, etc.

Le sugiero que comience con los siguientes enlaces y luego busque más en Google:

  • DevDays 2010: Anders Hejlsberg - C# 4.0 y posteriores
  • Canal 9: Mads Torgersen - Dentro de C# 4.0: escritura dinámica ++
  • DevX: COM Interop mejora mucho en C# 4.0
  • Scott Hanselman - C# 4 y la palabra clave dinámica - Whirlwind Tour por .NET 4 (y Visual Studio 2010) Beta 1
Lasse V. Karlsen avatar Apr 22 '2010 12:04 Lasse V. Karlsen

La palabra clave dinámica es nueva en C# 4.0 y se usa para indicarle al compilador que el tipo de una variable puede cambiar o que no se conoce hasta el tiempo de ejecución. Piensa en ello como si pudieras interactuar con un Objeto sin tener que lanzarlo.

dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!

Tenga en cuenta que no necesitamos emitir ni declarar cust como tipo Cliente. Debido a que lo declaramos dinámico, el tiempo de ejecución toma el control y luego busca y establece la propiedad FirstName por nosotros. Ahora, por supuesto, cuando utiliza una variable dinámica, está renunciando a la verificación del tipo del compilador. Esto significa que la llamada cust.MissingMethod() se compilará y no fallará hasta el tiempo de ejecución. El resultado de esta operación es una RuntimeBinderException porque MissingMethod no está definido en la clase Cliente.

El ejemplo anterior muestra cómo funciona la dinámica al llamar a métodos y propiedades. Otra característica poderosa (y potencialmente peligrosa) es la posibilidad de reutilizar variables para diferentes tipos de datos. Estoy seguro de que los programadores de Python, Ruby y Perl pueden pensar en un millón de formas de aprovechar esto, pero he estado usando C# durante tanto tiempo que me parece "incorrecto".

dynamic foo = 123;
foo = "bar";

Bien, lo más probable es que no escribas código como el anterior con mucha frecuencia. Sin embargo, puede haber ocasiones en las que la reutilización de variables pueda resultar útil o limpiar un fragmento sucio de código heredado. Un caso simple con el que me encuentro a menudo es tener que convertir constantemente entre decimal y doble.

decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");

La segunda línea no se compila porque 2.5 se escribe como doble y la línea 3 no se compila porque Math.Sqrt espera un doble. Obviamente, todo lo que tiene que hacer es emitir y/o cambiar el tipo de variable, pero puede haber situaciones en las que tenga sentido usar dinámica.

dynamic foo = GetDecimalValue(); // still returns a decimal
foo = foo / 2.5; // The runtime takes care of this for us
foo = Math.Sqrt(foo); // Again, the DLR works its magic
string bar = foo.ToString("c");

Leer más funciones: http://www.codeproject.com/KB/cs/CSharp4Features.aspx

Pranay Rana avatar Apr 22 '2010 12:04 Pranay Rana

Me sorprende que nadie haya mencionado el envío múltiple . La forma habitual de solucionar este problema es mediante el patrón Visitante y eso no siempre es posible, por lo que terminas con ischeques apilados.

Así que aquí hay un ejemplo de la vida real de una aplicación mía. En lugar de hacer:

public static MapDtoBase CreateDto(ChartItem item)
{
    if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item);
    if (item is MapPoint) return CreateDtoImpl((MapPoint)item);
    if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item);
    //other subtypes follow
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

Tú haces:

public static MapDtoBase CreateDto(ChartItem item)
{
    return CreateDtoImpl(item as dynamic);
}

private static MapDtoBase CreateDtoImpl(ChartItem item)
{
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

private static MapDtoBase CreateDtoImpl(MapPoint item)
{
    return new MapPointDto(item);
}

private static MapDtoBase CreateDtoImpl(ElevationPoint item)
{
    return new ElevationDto(item);
}

Tenga en cuenta que en el primer caso ElevationPointes una subclase de MapPointy si no se coloca antes MapPoint , nunca se alcanzará. Este no es el caso con la dinámica, ya que se llamará al método de coincidencia más cercano.

Como se puede adivinar por el código, esa característica fue útil mientras realizaba la traducción de objetos ChartItem a sus versiones serializables. No quería contaminar mi código con visitantes y tampoco quería contaminar mis ChartItemobjetos con atributos específicos de serialización inútiles.

Stelios Adamantidis avatar Nov 15 '2016 20:11 Stelios Adamantidis

Facilita la interoperabilidad de los lenguajes de tipo estático (CLR) con los dinámicos (python, ruby...) que se ejecutan en DLR (tiempo de ejecución de lenguaje dinámico), consulte MSDN :

Por ejemplo, podría utilizar el siguiente código para incrementar un contador en XML en C#.

Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);

Al usar el DLR, puede usar el siguiente código para la misma operación.

scriptobj.Count += 1;

MSDN enumera estas ventajas:

  • Simplifica la migración de lenguajes dinámicos a .NET Framework
  • Habilita funciones dinámicas en lenguajes escritos estáticamente
  • Proporciona beneficios futuros de DLR y .NET Framework
  • Permite compartir bibliotecas y objetos
  • Proporciona envío e invocación rápidos y dinámicos

Consulte MSDN para obtener más detalles.

Philip Daubmeier avatar Apr 22 '2010 12:04 Philip Daubmeier