C#: distribution d'exécution dynamique

Je voudrais implémenter une méthode avec la signature suivante

dynamic Cast(object obj, Type castTo);

Quelqu'un sait comment faire cela? obj implémente définitivement castTo mais doit être moulé correctement pour que certains des éléments de liaison d'exécution de mon application fonctionnent.

Edit: si certaines des réponses n'ont pas de sens, c'est parce que j'ai d'abord accidentellement tapé dynamic Cast(dynamic obj, Type castTo); - je veux dire que l'entrée devrait être object ou une autre classe de base garantie

47
demandé sur George Mauer 2011-02-07 22:43:30

7 réponses

Je pense que vous confondez les problèmes de casting et de conversion ici.

  • Casting: Le fait de changer le type d'une référence qui pointe vers un objet. Déplacer vers le haut ou vers le bas de la hiérarchie des objets ou vers une interface implémentée
  • conversion: créer un nouvel objet à partir de l'objet source d'origine d'un type différent et y accéder via une référence à ce type.

Il est souvent difficile de connaître la différence entre les 2 en C # parce que les deux parmi eux, utilisez le même opérateur C#: la distribution.

Dans cette situation, vous n'êtes presque certainement pas à la recherche d'une opération de distribution. La conversion d'un dynamic à un autre dynamic est essentiellement une conversion d'identité. Il ne fournit aucune valeur car vous obtenez simplement une référence dynamic vers le même objet sous-jacent. La recherche résultante ne serait pas différente.

Au lieu de cela, ce que vous semblez vouloir dans ce scénario est une conversion. C'est morphing l'objet sous jacent à un type différent et accéder à l'objet résultant de manière dynamic. La meilleure API pour cela est Convert.ChangeType.

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

Modifier

La question mise à jour a la ligne suivante:

Obj implémente définitivement castTo

Si c'est le cas, la méthode Cast n'a pas besoin d'exister. La source object peut simplement être affectée à une référence dynamic.

dynamic d = source;

Il semble que ce que vous essayez d'accomplir est de voir une interface ou un type particulier dans la hiérarchie de source à travers une référence dynamic. Ce n'est tout simplement pas possible. La référence dynamic résultante verra l'objet d'implémentation directement. Il ne regarde pas à travers un type particulier dans la hiérarchie de la source. Donc, l'idée de lancer un type différent dans la hiérarchie, puis de revenir à dynamic est exactement identique à l'attribution à dynamic en premier lieu. Il pointera toujours vers le même objet sous-jacent.

68
répondu JaredPar 2012-07-15 22:09:13

Cela devrait fonctionner:

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

Modifier

J'ai écrit le code de test suivant:

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

Cela ressemble au genre de "typecasting" que l'on trouve dans des langages comme PHP, JavaScript ou Python (car il convertit aussi la valeur en type désiré). Je ne sais pas si c'est une bonne chose, mais il fonctionne... :-)

25
répondu rsenna 2011-02-07 20:06:36

Le meilleur que j'ai obtenu jusqu'à présent:

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;
}
7
répondu George Mauer 2014-10-14 20:37:57

Le framework opensource Dynamitey a une méthode statique qui effectue une liaison tardive en utilisant DLR, y compris la conversion de conversion entre autres.

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

L'avantage de ceci par rapport à un Cast<T> appelé en utilisant la réflexion, est que cela fonctionnera également pour tout {[2] } qui a des opérateurs de conversion dynamiques, ie. TryConvert sur DynamicObject.

6
répondu jbtule 2015-03-21 17:48:57

Je me rends compte que cela a été répondu, mais j'ai utilisé une approche différente et j'ai pensé que cela pourrait valoir la peine d'être partagé. En outre, j'ai l'impression que mon approche pourrait produire des frais généraux indésirables. Cependant, je ne suis pas capable d'observer ou de calculer quelque chose qui se passe si mal sous les charges que nous observons. Je cherchais des commentaires utiles sur cette approche.

Le problème avec l'utilisation de dynamics est que vous ne pouvez attacher aucune fonction à l'objet dynamique directement. Vous devez utiliser quelque chose qui peut déterminez les tâches que vous ne voulez pas comprendre à chaque fois.

Lors de la planification de cette solution simple, j'ai regardé ce que sont les intermédiaires valides lors de la tentative de retaper des objets similaires. J'ai trouvé qu'un tableau binaire, une chaîne (xml, json) ou un codage en dur d'une conversion (IConvertable) étaient les approches habituelles. Je ne veux pas entrer dans les conversions binaires en raison d'un facteur de maintenabilité du code et de la paresse.

Ma théorie est que Newtonsoft pourrait le faire en utilisant un intermédiaire de chaîne.

En tant qu'inconvénient, je suis assez certain que lors de la conversion de la chaîne en un objet, il utiliserait la réflexion en recherchant dans l'assembly actuel un objet avec des propriétés correspondantes, créerait le type, puis instancierait les propriétés, ce qui nécessiterait plus de réflexion. Si true, tout cela peut être considéré comme une surcharge évitable.

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

Cela dit, cela correspond à un autre utilitaire que j'ai mis en place qui me permet de créer n'importe quel objet dans une dynamique. Je sais que j'ai dû utiliser la réflexion pour le faire correctement:

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

Je devais offrir cette fonction. Un objet arbitraire affecté à une variable typée dynamique ne peut pas être converti en IDictionary et rompt la fonction ConvertDynamic. Pour que cette chaîne fonctionnelle soit utilisée, il faut prévoir une dynamique de système.Dynamique.ExpandoObject, ou IDictionary .

3
répondu JRodd 2017-07-31 22:13:07

Essayez un Générique:

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

Ceci est au format de la méthode d'extension, donc son utilisation serait comme si elle était un membre d'objets dynamiques:

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

EDIT: Grr, ne l'a pas vu. Oui, vous pourriez fermer de manière réflective le générique, et il ne serait pas difficile de se cacher dans une méthode d'extension Non Générique:

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

Je ne suis pas sûr de ce que tu retirerais de ça. Fondamentalement, vous prenez une dynamique, forçant un cast à un type réfléchi, puis le bourrant dans une dynamique. Peut-être que vous avez raison, je ne posez pas de questions. Mais, cela fera probablement ce que vous voulez. Fondamentalement, lorsque vous allez dans dynamic-land, vous perdez le besoin d'effectuer la plupart des opérations de coulée car vous pouvez découvrir ce qu'est un objet et le fait à travers des méthodes réfléchissantes ou des essais et erreurs, donc il n'y a pas beaucoup de façons élégantes de le faire.

0
répondu KeithS 2011-02-07 20:02:43

Sinon:

public static T Cast<T>(this dynamic obj) where T:class
{
   return obj as T;
}
-2
répondu J R 2011-02-07 19:49:56