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




.net dynamic (7)

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?


  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


El mejor caso de uso de variables de tipo 'dinámico' para mí fue cuando, recientemente, estaba escribiendo una capa de acceso a datos en ADO.NET ( usando SQLDataReader ) y el código invocaba los procedimientos almacenados heredados ya escritos. Hay cientos de esos procedimientos almacenados heredados que contienen la mayor parte de la lógica comercial. Mi capa de acceso a datos necesitaba devolver algún tipo de datos estructurados a la capa de lógica de negocios, basada en C #, para hacer algunas manipulaciones ( aunque casi no hay ninguna ). Cada procedimiento almacenado devuelve un conjunto diferente de datos ( columnas de tabla ). Entonces, en lugar de crear docenas de clases o estructuras para contener los datos devueltos y pasarlos al BLL, escribí el siguiente código que se ve bastante elegante y ordenado.

public static dynamic GetSomeData(ParameterDTO dto)
        {
            dynamic result = null;
            string SPName = "a_legacy_stored_procedure";
            using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
            {
                SqlCommand command = new SqlCommand(SPName, connection);
                command.CommandType = System.Data.CommandType.StoredProcedure;                
                command.Parameters.Add(new SqlParameter("@empid", dto.EmpID));
                command.Parameters.Add(new SqlParameter("@deptid", dto.DeptID));
                connection.Open();
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        dynamic row = new ExpandoObject();
                        row.EmpName = reader["EmpFullName"].ToString();
                        row.DeptName = reader["DeptName"].ToString();
                        row.AnotherColumn = reader["AnotherColumn"].ToString();                        
                        result = row;
                    }
                }
            }
            return result;
        }

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 .


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


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


Me sorprende que nadie haya mencionado envíos múltiples . La forma habitual de evitar esto es a través del patrón de visitante y eso no siempre es posible, por lo que terminas con cheques 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ú lo 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, ElevationPoint es la subclase de MapPoint y, si no se coloca antes de MapPoint , nunca se alcanzará. Este no es el caso con la dinámica, ya que se llamará al método de comparación más cercano.

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


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;
    }




c#-4.0