2010-11-03 6 views
3

Je dois lancer un Action<string> à Action<object>. Bien que ce type soit dangereux en général, dans mon cas, il sera toujours appelé avec une chaîne. J'obtiens cette erreur:Action d'incantation <string> à l'action <object>

Unable to cast object of type 'System.Action1[System.String]' to type 'System.Action 1 [System.Object] '.

Des indices? La réflexion est un jeu juste. Envelopper le délégué dans un autre n'est pas.

MISE À JOUR: J'ai créé une nouvelle question à Creating an performant open delegate for an property setter or getter une meilleure explication de mon problème et une solution à l'aide d'emballage que je veux améliorer

+3

Désolé, mais le CLR n'a tout simplement aucun moyen de le faire sans envelopper un délégué avec un autre. – Gabe

+0

Il peut être utile d'inclure la raison pour laquelle vous essayez de le faire (travailler avec une bibliothèque tierce, curiosité, etc.) car, comme le mentionne Gabe, cela est impossible sans l'encapsuler dans un autre délégué. Il est cependant possible qu'il existe une solution à un problème sous-jacent que vous pourriez avoir. –

+0

Toute raison particulière pour s'opposer à l'emballage du délégué avec d'autres .. son très simple que la réflexion ... – RameshVel

Répondre

1

Je sais que votre message spécifie que l'emballage dans un autre délégué n'est pas une option, mais malheureusement c'est vraiment votre meilleur choix ici. Le CLR ne permet tout simplement pas une distribution entre les délégués dans cette direction. Aucune quantité de réflexion ne peut résoudre ce problème non plus. Pouvez-vous nous expliquer pourquoi ce n'est pas une option?

La raison pour laquelle il crée des problèmes de sécurité de type parce que l'appelant peut passer n'importe quel objet dans le Action<object> tandis que le Action<string> peut uniquement gérer les chaînes. Même si votre code ne passe que dans un string, le CLR ne peut pas le garantir et ne permet donc pas la conversion dangereuse.

La meilleure option suivante que je peux penser est de changer la méthode originale qui est enveloppée dans un Action<string> de prendre un paramètre de type string à celui qui prend object. Puis laissez-le vérifier manuellement le type est string.Par exemple

// Original Version 
void Method(string str) { 
    // Operate on the string 
} 

// Modified version 
void Method(object obj) { 
    string str = (string)obj; 
    // operate on the string 
} 
6

D'abord, ce n'est pas sûr. Quelque chose qui peut accepter n'importe quelle chaîne ne peut (nécessairement) accepter aucun objet. Pensez à un exemple méthode:

void method(String s) 
{ 
    s.Trim(); 
} 

De toute évidence, cela échouera si s étaient un objet sans Trim.

Techniquement, cela signifie Action est contravariant sur T. Vous pouvez attribuer un Action<string> à une référence Action<SubclassString> hypothétique, mais string ne peut pas être sous-classé.

Il est vrai que C# permet des conversions non sécurisées régulières (par exemple object lui-même à string), au risque d'un InvalidCastException. Cependant, ils ont choisi de ne pas implémenter l'infrastructure pour les distributions de délégué non sécurisées.

EDIT: Je ne connais pas un moyen de le faire sans wrapper.

+0

Bien sûr. J'ai ajouté une précision à la question. Mon contrat garantit qu'il ne sera appelé qu'avec une chaîne. Donc le casting est en effet ce dont j'ai besoin. –

+0

Un modérateur peut-il supprimer cette réponse? Cela ne m'aide pas et pourrait empêcher d'autres personnes d'examiner la question. –

+2

@David, quand j'ai répondu à la question, il a dit: «Je dois lancer un' Action 'à l'action . Il va de soi que cela devrait être possible» et j'ai expliqué pourquoi c'est faux. Même maintenant, le titre de la question n'est pas clair. –

0

Puisque la réflexion est juste jeu, je vais suggérer ce qui suit. Vous pouvez vous éloigner du problème de casting si vous gardez trace d'un objet, MethodInfo, et éventuellement du paramètre que vous allez exécuter. Un exemple de la façon dont le concept fonctionne comme suit:

Action<string> s; 
Action<object> o; 

object sTarget = s.Target; 
object oTarget = o.Target; 

MethodInfo sMethod = s.Method; 
MethodInfo oMethod = o.Method; 

// Time to invoke in a later time. 
sMethod.Invoke(sTarget, new object[] { strVal }); 
oMethod.Invoke(oTarget, new object[] { objVal }); 

Le seul danger avec c'est la possibilité que le type de mauvais paramètre sera exécuté sur la mauvaise méthode. Vous devrez peut-être faire de la comptabilité ici pour éviter cela. Mais, dans votre cas, puisqu'il y a une garantie qu'une chaîne va être passée (et puisqu'une chaîne est toujours un objet), cela devrait toujours réussir.

1

Désolé pour la bosse en retard, mais je cherchais un moyen de le faire aujourd'hui et sommes tombés sur ce poste. Vraiment, le seul moyen simple que j'ai trouvé pour le faire était d'envelopper Action<string> dans un nouveau Action<object>. Dans mon cas, je poussais mes Actions dans un Dictionnaire Concurrent, puis les récupérais par type.

En fait, je traitais une file d'attente de messages où des actions pouvaient être définies pour gérer des messages avec une entrée typée particulière.

ConcurrentDictionary<Type, Action<object>> _actions = new ConcurrentDictionary<Type, Action<object>>(); 
Action<string> actionStr = s => Console.WriteLine(s); 

var actionObj = new Action<object>(obj => { 
    var castObj = (V)Convert.ChangeType(obj, typeof(V)); actionStr(castObj); 
}); 

_actions.TryAdd(typeof(string), actionObj); 
Questions connexes