.net - ¿Para qué se utiliza el tipo "dinámico" en C#4.0?




dynamic c#-4.0 (9)

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

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


Answers

En su mayoría será utilizado por las víctimas de RAD y Python para destruir la calidad del código, IntelliSense y la detección de errores de tiempo de compilación.


  1. Puede llamar a lenguajes dinámicos como CPython usando pythonnet:

dynamic np = Py.Import("numpy")

  1. Puede aplicar genéricos a dynamic al aplicar operadores numéricos en ellos. Esto proporciona seguridad de tipo y evita las limitaciones de los genéricos. Esto es en esencia * tipa pato:

T y = x * (dynamic)x , donde typeof(x) is T


Interoperabilidad COM Especialmente IUnknown. Fue diseñado especialmente para eso.


Un ejemplo de uso:

Consume muchas clases que tienen una propiedad común 'CreationDate':

public class Contact
{
    // some properties

    public DateTime CreationDate { get; set; }        
}

public class Company
{
    // some properties

    public DateTime CreationDate { get; set; }

}

public class Opportunity
{
    // some properties

    public DateTime CreationDate { get; set; }

}

Si escribe un método común que recupera el valor de la propiedad 'CreationDate', debería usar reflection:

    static DateTime RetrieveValueOfCreationDate(Object item)
    {
        return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
    }

Con el concepto "dinámico", su código es mucho más elegante:

    static DateTime RetrieveValueOfCreationDate(dynamic item)
    {
        return item.CreationDate;
    }

Se evalúa en tiempo de ejecución, por lo que puede cambiar el tipo como puede en JavaScript a lo que desee. Esto es legitimo:

dynamic i = 12;
i = "text";

Y para que pueda cambiar el tipo que necesita. Úselo como último recurso; es beneficioso, pero he oído que muchas cosas suceden debajo de las escenas en términos de IL generado y que pueden tener un precio de rendimiento.


Hace que sea más fácil para los lenguajes de escritura estática (CLR) interoperar con los dinámicos (python, ruby ​​...) que se ejecutan en el DLR (tiempo de ejecución de lenguaje dinámico), consulte MSDN .


La palabra clave dynamic se agregó, junto con muchas otras características nuevas de C # 4.0, para que sea más sencillo hablar con un código que vive o proviene de otros tiempos de ejecución, que tiene diferentes API.

Toma un ejemplo.

Si tiene un objeto COM, como el objeto Word.Application , 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, esto 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);

Tenga en cuenta todos esos argumentos? Debe pasarlos desde C # antes de que la versión 4.0 no tenga una noción de argumentos opcionales. En C # 4.0, se ha hecho más fácil trabajar con las API COM introduciendo:

  1. Argumentos opcionales
  2. Haciendo la ref opcional para las API COM
  3. Argumentos con nombre

La nueva sintaxis para la llamada anterior sería:

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

Vea cuánto más fácil se ve, ¿cuánto más legible se vuelve?

Vamos a romper 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 antes, pero la sintaxis se ha ocultado, ahora puede enfocarse en el qué , y no tanto sobre cómo . A Anders Hejlsberg le gusta decir que tienes que invocar diferentes "hechizos", que 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 obtener un cierto tipo de hechizo. La antigua forma de API de hablar con objetos COM era mucho de eso, era necesario pasar por muchos aros para convencer al compilador de que compilara el código por usted.

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

Si no sabes de qué se trata, IDispatch es básicamente reflejo de los objetos COM. Con una interfaz IDispatch puede preguntar al objeto "cuál es el número de identificación para el método conocido como Guardar", y crear matrices de un cierto tipo que contiene los valores del argumento, y finalmente invocar un método Invoke en la interfaz IDispatch para llamar al método , pasando toda la información que ha logrado juntar.

El método Save 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 solo por abrir un documento.

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

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

Básicamente, es solo C # alcanzar a 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 .NET runtime.

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

Sin embargo, ¿qué pasa si quieres 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 dinámicos, debes 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, simplemente completamente diferente.

Y para Ruby? 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 requiere la palabra clave dynamic y asigna las llamadas al modo correcto de hacer las cosas. La API incluso está documentada, por lo que si tiene objetos que provienen de un tiempo de ejecución no cubierto, puede agregarlo.

Sin embargo, la palabra clave dynamic no pretende reemplazar ningún código existente de .NET. Claro, puedes hacerlo, pero no fue agregado por esa razón, y los autores del lenguaje de programación C # con Anders Hejlsberg al frente, han sido más firmes en que aún consideran 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 compilarlo, no fue una especie de tipo de sistema de magia que permite imaginar qué fue lo que querías decir en el tiempo de ejecución.

Todo el propósito fue facilitar el diálogo con otros tipos de objetos.

Hay un montón de material en Internet sobre la palabra clave, los defensores, los opositores, las discusiones, las difamaciones, los elogios, etc.

Te sugiero que comiences con los siguientes enlaces y luego busca más en Google:


La palabra clave dinámica es nueva en C # 4.0 y se usa para indicar al compilador que el tipo de variable puede cambiar o que no se conoce hasta el tiempo de ejecución. Piense que es capaz de 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. Como lo declaramos dinámico, el tiempo de ejecución toma el control y luego busca y establece la propiedad FirstName para nosotros. Ahora, por supuesto, cuando está usando una variable dinámica, está renunciando a la verificación del tipo de compilador. Esto significa que la llamada cust.MissingMethod () compilará y no fallará hasta el tiempo de ejecución. El resultado de esta operación es 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 poder 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 # por tanto tiempo que me parece "incorrecto".

dynamic foo = 123;
foo = "bar";

De acuerdo, probablemente no escriba código como el anterior muy a menudo. Sin embargo, puede haber ocasiones en que la reutilización variable puede ser útil o limpiar una pieza sucia de código heredado. Un caso simple con el que me encuentro a menudo es tener que lanzar 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 un doble y la línea 3 no se compila porque Math.Sqrt espera un doble. Obviamente, todo lo que tiene que hacer es transmitir y / o cambiar su tipo de variable, pero puede haber situaciones en las que tiene sentido usar la 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 característica: http://www.codeproject.com/KB/cs/CSharp4Features.aspx


Ya no sé de dónde saco la parte de esta extensión de enumeración, pero es de . ¡Lo siento por esto! Pero tomé éste y lo modifiqué para enums con Flags. Para enums con Flags hice esto:

  public static class Enum<T> where T : struct
  {
     private static readonly IEnumerable<T> All = Enum.GetValues(typeof (T)).Cast<T>();
     private static readonly Dictionary<int, T> Values = All.ToDictionary(k => Convert.ToInt32(k));

     public static T? CastOrNull(int value)
     {
        T foundValue;
        if (Values.TryGetValue(value, out foundValue))
        {
           return foundValue;
        }

        // For enums with Flags-Attribut.
        try
        {
           bool isFlag = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;
           if (isFlag)
           {
              int existingIntValue = 0;

              foreach (T t in Enum.GetValues(typeof(T)))
              {
                 if ((value & Convert.ToInt32(t)) > 0)
                 {
                    existingIntValue |= Convert.ToInt32(t);
                 }
              }
              if (existingIntValue == 0)
              {
                 return null;
              }

              return (T)(Enum.Parse(typeof(T), existingIntValue.ToString(), true));
           }
        }
        catch (Exception)
        {
           return null;
        }
        return null;
     }
  }

Ejemplo:

[Flags]
public enum PetType
{
  None = 0, Dog = 1, Cat = 2, Fish = 4, Bird = 8, Reptile = 16, Other = 32
};

integer values 
1=Dog;
13= Dog | Fish | Bird;
96= Other;
128= Null;




c# .net dynamic c#-4.0