Comment créer un object délégué dynamic par type en C #?

Disons que j’ai un type , que je sais être dérivé d’un type Delegate . J’aimerais créer un object de ce type en enveloppant un délégué anonyme qui accepte des parameters arbitraires et renvoie un object de type de retour correct:

 var retType = type.GetMethod("Invoke").ReturnType; var obj = Delegate.CreateDelegate(type, delegate(object[] args) { ... if (retType != typeof(void)) ... somehow create object of type retType and return it ... }); 

Évidemment, cela ne comstackra pas, car CreateDelegate attend un MethodInfo comme second argument. Comment puis-je faire cela correctement?

Mise à jour: Un peu plus d’informations sur ce que j’essaie de réaliser. Deux applications sont en cours d’exécution: client dans un navigateur et serveur en C #. Le navigateur peut appeler des fonctions distantes côté serveur en sérialisant les arguments en JSON et en envoyant l’appel sur le réseau (comme dans RPC). Cela fonctionne déjà, mais je voudrais append un support pour les rappels. Par exemple:

JavaScript (client):

 function onNewObject(uuid) { console.log(uuid); } server.notifyAboutNewObjects(onNewObject); 

C # (serveur):

 void notifyAboutNewObjects(Action callback) { ... callback("new-object-uuid"); ... } 

Le code middleware recevra un appel du navigateur et devra générer un faux délégué de callback qui renverra l’appel au callback et bloquera le fil jusqu’à son achèvement. Le code pour l’envoi / la réception existe déjà, je ne sais pas comment générer un délégué générique qui mettra simplement tous les arguments dans un tableau et les transmettra au code d’envoi.

Mise à jour: Si quelqu’un peut écrire du code qui générera un tel délégué au moment de l’exécution (par exemple, à l’aide de DynamicMethod ), je considérerai cela comme une réponse valide. Je n’ai tout simplement pas assez de temps pour apprendre à faire cela et j’espère qu’une personne expérimentée sera capable d’écrire ce code assez rapidement. Essentiellement, le code doit simplement prendre des parameters delegates arbitraires (la liste et les types sont disponibles au moment de l’exécution), les placer dans un tableau et appeler une méthode générique. La méthode générique renverra toujours un object , qui doit être converti en type de retour respectif ou ignoré si la fonction retourne void .

Uppdate: J’ai créé un petit programme de test qui montre ce dont j’ai besoin:

 using System; using System.Reflection; namespace TestDynamicDelegates { class MainClass { // Test function, for which we need to create default parameters. private static ssortingng Foobar(float x, Action a1, Func a2) { a1(42); return a2("test"); } // Delegate to represent generic function. private delegate object AnyFunc(params object[] args); // Construct a set of default parameters to be passed into a function. private static object[] ConstructParams(ParameterInfo[] paramInfos) { object[] methodParams = new object[paramInfos.Length]; for (var i = 0; i < paramInfos.Length; i++) { ParameterInfo paramInfo = paramInfos[i]; if (typeof(Delegate).IsAssignableFrom(paramInfo.ParameterType)) { // For delegate types we create a delegate that maps onto a generic function. Type retType = paramInfo.ParameterType.GetMethod("Invoke").ReturnType; // Generic function that will simply print arguments and create default return value (or return null // if return type is void). AnyFunc tmpObj = delegate(object[] args) { Console.WriteLine("Invoked dynamic delegate with following parameters:"); for (var j = 0; j < args.Length; j++) Console.WriteLine(" {0}: {1}", j, args[j]); if (retType != typeof(void)) return Activator.CreateInstance(retType); return null; }; // Convert generic function to the required delegate type. methodParams[i] = /* somehow cast tmpObj into paramInfo.ParameterType */ } else { // For all other argument type we create a default value. methodParams[i] = Activator.CreateInstance(paramInfo.ParameterType); } } return methodParams; } public static void Main(string[] args) { Delegate d = (Func<float, Action,Func,ssortingng>)Foobar; ParameterInfo[] paramInfo = d.Method.GetParameters(); object[] methodParams = ConstructParams(paramInfo); Console.WriteLine("{0} returned: {1}", d.Method.Name, d.DynamicInvoke(methodParams)); } } } 

J’ai écrit une bibliothèque PCL opensource, appelée Dynamitey (en nuget), qui effectue toutes sortes de tâches dynamics à l’aide du C # DLR. .

Il possède en particulier une méthode statique appelée Dynamic.CoerceToDelegate(object invokeableObject, Type delegateType) qui Dynamic.CoerceToDelegate(object invokeableObject, Type delegateType) fondamentalement l’appel dynamic d’un object DynamicObject ou d’un délégué plus général, avec le type spécifique de délégué utilisant ComstackdExpressions ( source ).

en utilisant System.Dynamic, vous pouvez créer un object invocable:

 public class AnyInvokeObject:DynamicObject{ Func _func; public AnyInvokeObject(Func func){ _func = func; } public override bool TryInvoke(InvokeBinder binder, object[] args, out object result){ result = _func(args); return true; } } 

Puis dans votre échantillon:

 var tmpObj = new AnyInvokeObject(args => { Console.WriteLine("Invoked dynamic delegate with following parameters:"); for (var j = 0; j < args.Length; j++) Console.WriteLine(" {0}: {1}", j, args[j]); if (retType != typeof(void)) return Activator.CreateInstance(retType); return null; }); methodParams[i] = Dynamic.CoerceToDelegate(tmpObj, paramInfo.ParameterType); 

Que diriez-vous d’une solution sans delegates? (avertissement: pseudocode rampant)

 class AbstractServerToClientMessage { public virtual ssortingng ToJSON(); } class OnNewObjectMessage: AbstractServerToClientMessage {...} class OnSomethingElseHappenedMessage: AbstractServerToClientMessage {...} void NotifyClient(AbstractServerToClientMessage message) event OnNewObject; event OnSomethingElseHappened; void notifyAboutNewObjects() { ... OnNewObject += NotifyClient; ... } void AddNewObject(SomeObject obj) { OnNewObjectMessage message(obj); OnNewObject(message); // actually add the object } 

les messages seront quand même sérialisés, alors pourquoi s’embêter? Le polymorphism se chargera du rest. La seule exigence est d’avoir un ensemble de messages correspondant à chaque type d’événement.

Le renvoi de la valeur peut être implémenté en écrivant dans un champ de AbstractServerToClientMessage .


Ou bien, vous pouvez réellement avoir un délégué avec une signature fixe acceptant un AbstractServerToClientMessage similaire. Encore une fois, le polymorphism (+ une fabrique de classes pour la désérialisation) permettra de le transtyper pour corriger le type de message.

Vous pouvez soit consulter le code source de SignalR, soit simplement l’utiliser.

Enregistrer une fonction dans le navigateur que le serveur peut appeler

 var connection = $.hubConnection(); var yourHubProxy = connection.createHubProxy('yourHub'); yourHubProxy.on('addMessageToConsole', function (message) { console.log(message); }); 

et sur le serveur

 public class YourHub : Hub { public void SendMessage(ssortingng message) { Clients.All.addMessageToConsole(message); } } 

Voir ici pour plus d’exemples.