Dinámico en C #: recetas de uso

Esta es la parte final de la serie Dynamic Language Runtime . Artículos anteriores:

  1. Detalle dinámico: juegos encubiertos del compilador, pérdida de memoria, matices de rendimiento . Este artículo analiza la caché DLR en detalle y los puntos que son importantes para el desarrollador.
  2. Grokl DLR . Una descripción general de la tecnología, la disección de DynamicMetaObject y una breve instrucción sobre cómo crear su propia clase dinámica.

En este breve artículo, finalmente analizaremos los principales casos de uso dinámico en la vida real: cuándo no puede prescindir de él y cuándo puede hacer la vida mucho más fácil.



Cuando la dinámica es indispensable


No hay tales casos. Siempre puede escribir código de funcionalidad similar en un estilo estático, la única diferencia es la facilidad de lectura y la cantidad de código. Por ejemplo, cuando trabaje con objetos COM, en lugar de dinámico, puede usar la reflexión.

Cuando dinámico es útil


Trabajar con objetos COM


En primer lugar, esto, por supuesto, es trabajar con objetos COM, por lo cual se inició todo esto. Compare el código obtenido con dinámica y reflexión:

dynamic instance = Activator.CreateInstance(type); instance.Run("Notepad.exe"); 

 var instance = Activator.CreateInstance(type); type.InvokeMember("Run", BindingFlags.InvokeMethod, null, instance, new[] { "Notepad.exe" }); 

Como regla general, para trabajar con objetos COM a través de la reflexión, debe crear clases ramificadas con contenedores para cada método / propiedad. También hay cosas menos obvias, como la capacidad de no completar los parámetros que no necesita (obligatorio desde el punto de vista de un objeto COM) al llamar a un método a través de la dinámica .

Trabajar con configuraciones


Otro ejemplo de libro de texto está trabajando con configuraciones, como XML . Sin dinámica :

 XElement person = XElement.Parse(xml); Console.WriteLine( $"{person.Descendants("FirstName").FirstOrDefault().Value} {person.Descendants("LastName").FirstOrDefault().Value}" ); 

Con dinámica:

 dynamic person = DynamicXml.Parse(xml); Console.WriteLine( $"{person.FirstName} {person.LastName}" ); 

Por supuesto, para esto necesita implementar su propia clase dinámica. Como alternativa a la primera lista, puede escribir una clase que funcione de la siguiente manera:

 var person = StaticXml.Parse(xml); Console.WriteLine( $"{person.GetElement("FirstName")} {person.GetElement("LastName")}" ); 

Pero, ya ves, esto se ve mucho menos elegante que a través de la dinámica .

Trabajar con recursos externos.


El párrafo anterior se puede generalizar a cualquier acción con recursos externos. Siempre tenemos dos alternativas: usar dinámico para obtener el código en estilo nativo de C # o escribir estático con "líneas mágicas". Veamos un ejemplo con una solicitud de API REST . Con Dynamic, puedes escribir esto:

 dynamic dynamicRestApiClient = new DynamicRestApiClient("http://localhost:18457/api"); dynamic catsList = dynamicRestApiClient.CatsList; 

Donde nuestra clase dinámica enviará una solicitud del formulario a solicitud de la propiedad

 [GET] http://localhost:18457/api/catslist 

Luego lo deserializa y nos devuelve una serie de gatos que ya están listos para el uso previsto. Sin dinámica, se vería así:

 var restApiClient = new RestApiClient("http://localhost:18457/api"); var catsListJson = restApiClient.Get("catsList"); var deserializedCatsList = JsonConvert.DeserializeObject<Cat[]>(catsListJson); 

Reemplazo de reflejo


En el ejemplo anterior, puede tener una pregunta: ¿por qué en un caso estamos deserializando el valor de retorno a un tipo específico, y en el otro no? El hecho es que en la escritura estática necesitamos convertir explícitamente objetos al tipo Cat para trabajar con ellos. En el caso de la dinámica , es suficiente deserializar JSON en una matriz de objetos dentro de nuestra clase dinámica y devolver el objeto [] , ya que dinámica se encarga de la reflexión. Daré dos ejemplos de cómo funciona esto:

 dynamic deserialized = JsonConvert.DeserializeObject<object>(serialized); var name = deserialized.Name; var lastName = deserialized.LastName; 

 Attribute[] attributes = type.GetCustomAttributes(false).OfType<Attribute>(); dynamic attribute = attributes.Single(x => x.GetType().Name == "DescriptionAttribute"); var description = attribute.Description; 

El mismo principio que cuando se trabaja con objetos COM.

Visitante


Usando dinámico, puede implementar de manera muy elegante este patrón. En lugar de mil palabras:

 public static void DoSomeWork(Item item) { InternalDoSomeWork((dynamic) item); } private static void InternalDoSomeWork(Item item) { throw new Exception("Couldn't find handler for " + item.GetType()); } private static void InternalDoSomeWork(Sword item) { //do some work with sword } private static void InternalDoSomeWork(Shield item) { //do some work with shield } public class Item { } public class Sword : Item {} public class Shield : Item {} 

Ahora, al pasar un objeto de tipo Sword al método DoSomeWork , se llamará al método InternalDoSomeWork (elemento Sword) .

Conclusiones


Ventajas de usar dinámico :

  • Se puede usar para la creación rápida de prototipos: en la mayoría de los casos, el número de código repetitivo disminuye
  • Como regla, mejora la legibilidad y la estética (debido a la transición de "líneas mágicas" al estilo nativo del lenguaje) del código
  • A pesar de la opinión generalizada, gracias a los mecanismos de almacenamiento en caché, no surge una sobrecarga de rendimiento significativa en el caso general

Contras de usar dinámico:

  • Hay matices no evidentes asociados con la memoria y el rendimiento.
  • Con el apoyo y la lectura de tales clases dinámicas, debe comprender bien lo que está sucediendo.
  • El programador está privado de la verificación de tipo y de todas las garantías de salud proporcionadas por el compilador.

Conclusión


En mi opinión, el desarrollador recibirá el mayor beneficio del uso dinámico en las siguientes situaciones:

  • Cuando prototipos
  • En proyectos pequeños / domésticos donde el costo del error es bajo
  • En pequeñas utilidades de tamaño de código que no implican un tiempo de ejecución prolongado. Si su utilidad se ejecuta en el peor de los casos durante unos segundos, generalmente no hay necesidad de pensar en pérdidas de memoria y degradación del rendimiento

Al menos controvertido es el uso de la dinámica en proyectos complejos con una gran base de código; aquí es mejor pasar tiempo escribiendo envoltorios estáticos, minimizando así la cantidad de momentos no obvios.

Si trabaja con objetos COM o dominios en servicios / productos que implican un tiempo de trabajo largo y continuo, es mejor no utilizar la dinámica , a pesar del hecho de que se creó para tales casos. Incluso si sabe a fondo qué y cómo hacer y nunca comete errores, tarde o temprano puede llegar un nuevo desarrollador que no lo sabe. Es probable que el resultado sea una pérdida de memoria difícil de calcular.

Source: https://habr.com/ru/post/470355/


All Articles