web-dev-qa-db-fra.com

C #: distribution dynamique à l'exécution

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 correctement lancé pour que certaines des liaisons exécutables de mon application fonctionnent.

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

58
George Mauer

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

  • Casting: Action de changer le type d'une référence qui pointe vers un objet. Soit en montant ou en descendant la hiérarchie des objets, soit en une interface implémentée
  • Conversion: création d'un nouvel objet à partir de l'objet source d'origine d'un type différent et accès à celui-ci via une référence à ce type.

Il est souvent difficile de connaître la différence entre les 2 en C #, car ils utilisent le même opérateur C #: la distribution.

Dans cette situation, vous n’êtes certainement pas à la recherche d’une opération de casting. La conversion de dynamic vers 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. Cela consiste à transformer l'objet sous-jacent en 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);
}

EDIT

La question mise à jour a la ligne suivante:

obj implémente définitivement castTo

Si tel 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 résultante dynamic verra directement l'objet d'implémentation. Il ne regarde pas à travers un type particulier dans la hiérarchie de la source. Donc, l'idée de transtyper vers un type différent dans la hiérarchie, puis de revenir à dynamic est exactement identique à une simple affectation à dynamic en premier lieu. Il indiquera toujours le même objet sous-jacent.

82
JaredPar

Cela devrait fonctionner:

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

Éditer

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 a également converti la valeur au type souhaité). Je ne sais pas si c'est une bonne chose, mais cela fonctionne certainement ... :-)

30
rsenna

Le mieux que j'ai eu jusqu'ici:

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;
}
8
George Mauer

Le framework opensource Dynamitey possède une méthode statique qui lie tardivement à l'aide de 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 sur un Cast<T> appelé en utilisant la réflexion, est que cela fonctionnera également pour tout IDynamicMetaObjectProvider qui a des opérateurs de conversion dynamiques, c'est-à-dire. TryConvert on DynamicObject.

7
jbtule

Je me rends compte que l'on a répondu à cette question, mais j'ai utilisé une approche différente et je pense que cela pourrait valoir la peine d'être partagé. De plus, j’ai le sentiment que mon approche pourrait produire des frais généraux non désirés. Cependant, je ne suis pas en mesure d'observer ou de calculer quoi que ce soit 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 la dynamique est qu'il est impossible d'attacher directement des fonctions à l'objet dynamique. Vous devez utiliser quelque chose qui peut déterminer les tâches que vous ne voulez pas déterminer à chaque fois.

Lors de la planification de cette solution simple, j'ai examiné quels étaient les intermédiaires valides lorsque vous tentez de retaper des objets similaires. J'ai trouvé qu'un tableau binaire, une chaîne (xml, json) ou une conversion codée en dur ( 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 était que Newtonsoft pourrait le faire en utilisant un intermédiaire de chaîne.

Par contre, je suis à peu près certain que lors de la conversion de la chaîne en objet, il utiliserait la réflexion en recherchant un objet avec des propriétés correspondantes dans l'assembly en cours, créerait le type, puis instancierait les propriétés, ce qui nécessiterait davantage de réflexion. Si cela est vrai, 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 au point qui me permet de transformer n'importe quel objet en dynamique. Je sais que je devais 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 de fonctions soit utilisée, une dynamique de System.Dynamic.ExpandoObject ou IDictionary <string, object> doit être fournie.

6
JRodd

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 méthode d'extension, donc son utilisation serait comme si elle était membre d'objets dynamiques:

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

EDIT: Grr, je n'ai pas vu ça. Oui, vous pouvez fermer le générique de manière réfléchie 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 tout simplement pas sûr de ce que vous en retireriez. Fondamentalement, vous prenez une dynamique, forçant une distribution à un type réfléchi, puis vous l'insérez dans une dynamique. Peut-être que vous avez raison, je ne devrais pas demander. Mais ça fera probablement ce que vous voulez. Lorsque vous entrez dans Dynamic Land, vous perdez le besoin de réaliser la plupart des opérations de casting, car vous pouvez découvrir ce qu'est un objet et ce qu'il fait par le biais de méthodes de réflexion ou d'essais et erreurs. Il n'y a donc pas beaucoup de façons élégantes de le faire.

1
KeithS