2011-02-07 21 views
38

me gustaría poner en práctica un método con la siguiente firmaC#: elenco de tiempo de ejecución dinámico

dynamic Cast(object obj, Type castTo); 

Alguien sabe cómo hacer eso? obj definitivamente implementa castTo pero necesita ser lanzado correctamente para que algunas cosas de encuadernación en tiempo de ejecución de mi aplicación funcionen.

Editar: Si alguna de las respuestas no tienen sentido que es porque inicialmente accidentalmente mecanografié dynamic Cast(dynamic obj, Type castTo); - me refiero a la entrada debe ser object o clase alguna otra base garantizada

+0

¿Está diciendo que necesita llamar dinámicamente a un operador de conversión implícita o explícita? – Gabe

+0

cualquiera de los dos, explícita –

Respuesta

58

Creo que estás confundiendo los problemas de conversión y conversión aquí.

  • Fundición: El acto de cambiar el tipo de una referencia que apunta a un objeto. Ya sea moviendo hacia arriba o hacia abajo la jerarquía de objetos o a una interfaz implementada
  • Conversión: Crea un nuevo objeto desde el objeto fuente original de un tipo diferente y accediendo a él a través de una referencia a ese tipo.

A menudo es difícil saber la diferencia entre los 2 en C# porque ambos usan el mismo operador de C#: el elenco.

En esta situación, casi con seguridad no está buscando una operación de conversión.Lanzar un dynamic a otro dynamic es esencialmente una conversión de identidad. No proporciona ningún valor porque solo está obteniendo una referencia dynamic de nuevo al mismo objeto subyacente. La búsqueda resultante no sería diferente.

En cambio, lo que parece querer en este escenario es una conversión. Eso está transformando el objeto subyacente en un tipo diferente y accediendo al objeto resultante de una manera dynamic. La mejor API para esto es Convert.ChangeType.

public static dynamic Convert(dynamic source, Type dest) { 
    return Convert.ChangeType(source, dest); 
} 

EDITAR

La pregunta actualizada tiene la siguiente línea:

OBJ definitivamente implementa castTo

Si este es el caso, entonces el método no Cast necesito existir La fuente object se puede asignar simplemente a una referencia dynamic.

dynamic d = source; 

suena como lo que estamos tratando de lograr es ver a un tipo particular de interfaz o en la jerarquía de source través de una referencia dynamic. Eso simplemente no es posible. La referencia dynamic resultante verá el objeto de implementación directamente. No mira a través de ningún tipo particular en la jerarquía de la fuente. Por lo tanto, la idea de convertir a un tipo diferente en la jerarquía y luego de vuelta a dynamic es exactamente idéntica a la asignación a dynamic en primer lugar. Seguirá apuntando al mismo objeto subyacente.

+0

Hola, Jared, estoy buscando un casting, pero si entiendes un punto, escribí mal la pregunta, debería ser desde el objeto (o alguna otra clase base) hasta el dinámico. Lo corregirá ahora –

+1

@George en ese caso, ¿por qué no simplemente lanzar a 'dynamic' directamente? No debería haber una necesidad de este método si la operación subyacente es realmente un molde. – JaredPar

+0

el caso de uso es un objeto de BaseType pasado, y tengo un IList manejadores donde cada manejador implementa IHandle donde T: BaseType. Necesito ejecutar todos los controladores que se aplican a este tipo particular ya que dynamic no puede adivinar correctamente (podría ser porque IHandle es covariante). Lo hice funcionar utilizando la reflexión, pero oy vey. –

0

Pruebe un genérico:

public static T CastTo<T>(this dynamic obj, bool safeCast) where T:class 
{ 
    try 
    { 
     return (T)obj; 
    } 
    catch 
    { 
     if(safeCast) return null; 
     else throw; 
    } 
} 

Esto está en formato método de extensión, por lo que su uso sería como si fuera un miembro de objetos dinámicos:

dynamic myDynamic = new Something(); 
var typedObject = myDynamic.CastTo<Something>(false); 

EDIT: Grr, no vio eso. Sí, se podría cerrar reflexivamente el genérico, y no sería difícil de ocultar en un método de extensión no genérico:

public static dynamic DynamicCastTo(this dynamic obj, Type castTo, bool safeCast) 
{ 
    MethodInfo castMethod = this.GetType().GetMethod("CastTo").MakeGenericMethod(castTo); 
    return castMethod.Invoke(null, new object[] { obj, safeCast }); 
} 

No estoy seguro de lo que se obtendría de esto. Básicamente estás tomando una dinámica, forzando un lanzamiento a un tipo reflejado, y luego rellenándolo en una dinámica. Quizás tengas razón, no debería preguntar. Pero, esto probablemente hará lo que quieras. Básicamente, cuando ingresas a tierra dinámica, pierdes la necesidad de realizar la mayoría de las operaciones de conversión, ya que puedes descubrir qué es y qué hace un objeto a través de métodos de reflexión o prueba y error, por lo que no hay muchas maneras elegantes de hacerlo.

+1

Esto no funciona (sin reflexión) si 'T' no se conoce en el tipo de compilación. – jason

+0

@ KiethS, tipo se proporciona durante el tiempo de ejecución. Lo sé, no preguntes. Supongo que podría escribir esto y usar la reflexión para cerrar el genérico, pero tiene que haber una manera mejor. –

+1

@George: No creo que puedas moverte usando la reflexión aquí. –

-2

alternativa:

public static T Cast<T>(this dynamic obj) where T:class 
{ 
    return obj as T; 
} 
+2

El requisito parece ser que el tipo se especifica en tiempo de ejecución. – Jimmy

20

Esto debería funcionar:

public static dynamic Cast(dynamic obj, Type castTo) 
{ 
    return Convert.ChangeType(obj, castTo); 
} 

Editar

He escrito el siguiente código de prueba:

var x = "123"; 
var y = Cast(x, typeof(int)); 
var z = y + 7; 
var w = Cast(z, typeof(string)); // w == "130" 

Se asemeja al tipo de "encasillamiento" que se encuentra en lenguajes como PHP, JavaScript o Python (porque también convierte el valor al tipo deseado). No sé si eso es algo bueno, pero ciertamente funciona ... :-)

+0

castTo tiene que describir un ValueType para que esto funcione. – KeithS

+0

Hmm, a punto de intentarlo, pero ¿no sería esto siempre devolver este abatimiento a objeto? –

+0

@Keith: acaba de probarlo, y funciona con 'String' (que es una clase, no un tipo de valor). – rsenna

7

mejor que tengo hasta ahora:

dynamic DynamicCast(object entity, Type to) 
{ 
    var openCast = this.GetType().GetMethod("Cast", BindingFlags.Static | BindingFlags.NonPublic); 
    var closeCast = openCast.MakeGenericMethod(to); 
    return closeCast.Invoke(entity, new[] { entity }); 
} 
static T Cast<T>(object entity) where T : class 
{ 
    return entity as T; 
} 
+0

Más o menos con lo que terminé después de tu comentario inicial. – KeithS

+2

No tiene sentido para mí. ¿Cómo es la llamada 'DynamicCast (obj, typeof (Foo))' diferente de 'obj' (dinámica), excepto que se obtiene una referencia nula en el caso en que' obj' (que tiene *** tiempo de compilación ** * tipo 'objeto') pasa a no ser un' Foo'? Además, la restricción 'donde T: objeto' no tiene sentido y no se permite. –

+1

@JeppeStigNielsen Es una cuestión de boxeo. Supongamos que tiene 'Persona' con un método' GetFullName' y 'Professor: Person' que anula' GetFullName' para que tenga el sufijo '' PhD ''. Ahora supongamos que esa entidad es del tipo 'Profesor' pero que se emite como' Persona' fuera de nuestro método, tal vez así sea cargada por un ORM o una carpeta modelo o algo así. si llama a '((dynamic) entitity) .GetFullName()' ejecutará 'Person.GetFullName()'. Ha pasado mucho tiempo desde que surgió este problema para mí, así que los detalles podrían estar un poco mal, pero tenía que ver con esto. –

6

El marco de código abierto Dynamitey tiene un método estático que hace el enlace en tiempo usando DLR incluyendo la conversión fundido entre others.

dynamic Cast(object obj, Type castTo){ 
    return Dynamic.InvokeConvert(obj, castTo, explict:true); 
} 

La ventaja de este sobre un Cast<T> llamada utilizando la reflexión, es que esto también funcionará para cualquier IDynamicMetaObjectProvider que tiene operadores de conversión dinámicos, es decir . TryConvert en DynamicObject.

3

Me doy cuenta de que esto ha sido respondido, pero utilicé un enfoque diferente y pensé que podría valer la pena compartirlo. Además, siento que mi enfoque puede producir gastos indirectos no deseados. Sin embargo, no soy capaz de observar o calcular todo lo que sucede que es malo bajo las cargas que observamos. Estaba buscando comentarios útiles sobre este enfoque.

El problema con el trabajo con dinámica es que no puede adjuntar ninguna función directamente al objeto dinámico. Tienes que usar algo que pueda descifrar las asignaciones que no quieras resolver cada vez.

Al planear esta solución simple, analicé cuáles son los intermediarios válidos cuando intento volver a escribir objetos similares. Encontré que una matriz binaria, una cadena (xml, json) o una conversión de codificación dura (IConvertable) eran los enfoques habituales. No quiero entrar en las conversiones binarias debido a un factor de mantenimiento del código y la pereza.

Mi teoría era que Newtonsoft podría hacer esto mediante el uso de un intermediario de cadena.

Como desventaja, estoy bastante seguro de que al convertir la cadena a un objeto, que usaría la reflexión buscando en el ensamblaje actual un objeto con propiedades coincidentes, cree el tipo, luego instale las propiedades, lo que requeriría más reflexión. Si es verdadero, todo esto puede considerarse una carga indirecta evitable.

C#:

//This lives in a helper class 
public static ConvertDynamic<T>(dynamic data) 
{ 
    return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(Newtonsoft.Json.JsonConvert.SerializeObject(data)); 
} 

//Same helper, but in an extension class (public static class), 
//but could be in a base class also. 
public static ToModelList<T>(this List<dynamic> list) 
{ 
    List<T> retList = new List<T>(); 
    foreach(dynamic d in list) 
    { 
     retList.Add(ConvertDynamic<T>(d)); 
    } 
} 

Con eso dicho, esto encaja otra utilidad he juntado que me permite hacer cualquier objeto en una dinámica. Sé que tenía que usar el reflejo para hacerlo correctamente:

public static dynamic ToDynamic(this object value) 
{ 
    IDictionary<string, object> expando = new ExpandoObject(); 

    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType())) 
     expando.Add(property.Name, property.GetValue(value)); 

    return expando as ExpandoObject; 
} 

Tenía que ofrecer esa función. Un objeto arbitrario asignado a una variable de tipo dinámico no se puede convertir a IDictionary, y romperá la función ConvertDynamic. Para que se use esta cadena de funciones, se debe proporcionar una dinámica de System.Dynamic.ExpandoObject o IDictionary < string, objeto >.

Cuestiones relacionadas