2010-08-08 13 views
22

Nella mia architettura di plugin sto passando un nome di plugin (stringa), nome del metodo (stringa) e parametri (array di oggetti) al mio servizio di plugin per eseguire il metodo specificato e restituire il risultato (di tipo T).Passaggio di tipo anonimo come parametri del metodo

metodo di esecuzione del servizio di plugin può essere visto sotto:

public TResult Execute<TResult>(string pluginName, string operation, params object[] input) { 
    MethodInfo method = null; 
    TResult result = default(TResult); 

    var plugin = _plugins.Enabled().FirstOrDefault(x => x.GetType().Name.Equals(pluginName, StringComparison.InvariantCultureIgnoreCase)); 

    if (plugin != null) { 
     method = plugin.GetType().GetMethods().FirstOrDefault(x => x.Name == operation); 
     if (method != null) { 
      result = (TResult)method.Invoke(plugin, input); 
     } 
    } 
    return result; 
    } 

Un esempio d'uso:

var url = AppHelper.PluginService.Execute<string>(
    "ImagePlugin", 
    "GetImageUrl", 
    new object[] { image, size }); 

Quello che vorrei fare è passare in un tipo anonimo, invece (come credo che questo sia più leggibile) vale a dire

var url = AppHelper.PluginService.Execute<string>(
    "ImagePlugin", 
    "GetImageUrl", 
    new { image = image, targetSize = size }); 

Come si modifica il metodo Execute per mappare l'anonimo t proprietà ype ai miei parametri del metodo plugin?

Avevo preso in considerazione l'utilizzo del nuovo tipo dinamico in .net 4.0 ma preferisco definire i miei parametri sul metodo plugin piuttosto che accettare un oggetto dinamico.

Grazie Ben

[Update]

Dopo aver guardato attraverso il codice sorgente di ASP.NET MVC sembra abbastanza semplice per tirare il tipo anonimo in un dizionario oggetti per esempio RouteValueDictionary. Con l'aiuto della riflessione viene creata un'espressione linq in modo dinamico. Sebbene sia una buona implementazione, non volevo davvero tutta questa complessità extra.

Come per il commento qui sotto, posso raggiungere la leggibilità semplicemente specificando i miei parametri in linea (non è necessario per la dichiarazione di matrice oggetto):

var url = AppHelper.PluginService.Execute<string>("ImagePlugin", "GetImageUrl", image, size); 
+2

Poiché si utilizza la parola chiave 'params', si può fare' immagine, dimensione' invece di 'nuovo oggetto [] {immagine, dimensione}'. Lo renderebbe più leggibile, e dato che i metodi 'Invoke' accettano array di oggetti, lascerei la firma del metodo così com'è. – Necros

risposta

9

Ci sono alcuni modi per rendere ciò possibile anche se non consiglierei nessuno di loro.

In primo luogo, è possibile utilizzare il riflesso che significa che è necessario scrivere un sacco di codice aggiuntivo (soggetto a errori) nel metodo PluginService.Execute per ottenere i valori desiderati.

In secondo luogo, se si conoscono i parametri del tipo anonimo che si sta passando al metodo, è possibile utilizzare la tecnica descritta here. Puoi eseguire il cast su un altro tipo anonimo all'interno del tuo metodo che ha le stesse proprietà. Here è un'altra descrizione della stessa tecnica di Jon Skeet.

In terzo luogo, è possibile utilizzare classi dallo System.ComponentModel.Ad esempio, ASP.NET MVC utilizza questo. Usa la riflessione sotto il cofano. Tuttavia, in ASP.NET MVC i nomi delle proprietà sono ben noti (controller e action per esempio) o il loro nome non ha importanza perché vengono passati così come sono ad un metodo di controller (id per esempio).

+0

Ronald, grazie per il collegamenti. Iniziando a pensare che dovrei attenermi alla mia attuale implementazione. Tuttavia, non è questo qualcosa che viene utilizzato estesamente in ASP.NET MVC, ad esempio negli helper HTML e nel routing. Sono sicuro che ci dovrebbe essere un modo "buono" per farlo altrimenti Microsoft non l'avrebbe fatto :) –

+0

Buon punto. Ho aggiornato la mia risposta con un altro modo di fare quello che vuoi. –

+0

Ho contrassegnato la risposta come la risposta più completa.Tuttavia, dopo aver esaminato il codice sorgente MVC per vedere come stanno raggiungendo questo obiettivo, seguirò la mia implementazione, ma uso il suggerimento di Necros per migliorare la leggibilità. –

1

ho fatto questo una volta. Quello che puoi fare è ottenere i parametri attesi dalla funzione attraverso la riflessione. Quindi, è possibile creare l'array di parametri facendo corrispondere i nomi nella serie di parametri con le chiavi dell'oggetto anonimo.

Spero che questo aiuti :-).

0

Prima di tutto, controlla lo spazio dei nomi System.Addin, potresti ricevere aiuto.

In secondo luogo, è possibile creare un'interfaccia personalizzata con nome e parametri specifici del metodo e consentire al plug-in di implementare l'interfaccia. È possibile definire l'interfaccia del plugin in un progetto diverso a cui è possibile fare riferimento sia nell'applicazione che nel progetto del plugin.

+0

Stiamo già utilizzando le interfacce insieme a StructureMap per chiamare specifici tipi di plugin. Questo è più per plugin che l'utente può creare al volo e desidera utilizzare all'interno dell'interfaccia utente (ad esempio, potrebbe voler scambiare l'ImagePlugin sopra per uno che estrae immagini da Amazon S3) –

21

Alla fine ho trovato this post che dimostra l'utilizzo di tipi anonimi come dizionari. Usando questo metodo puoi passare il tipo anonimo come parametro di metodo (oggetto) e accedere alle sue proprietà.

Tuttavia, vorrei anche aggiungere che dopo aver guardato nelle nuove caratteristiche dinamiche in .net 4.0, come l'ExpandoObject, ci si sente molto più pulito di passare un oggetto dinamico come parametro:

 dynamic myobj = new ExpandoObject(); 
     myobj.FirstName = "John"; 
     myobj.LastName = "Smith"; 

     SayHello(myobj); 
     ........... 

     public static void SayHello(dynamic properties) 
     { 
      Console.WriteLine(properties.FirstName + " " + properties.LastName); 
     } 
12

Utilizzare l'oggetto dinamico per i parametri se si desidera passare un tipo anonimo. Il metodo di esecuzione di un plug-in dovrebbe prevedere determinate proprietà di un oggetto parametro per poter funzionare. Usando il compilatore C# della parola chiave dinamica verrà richiesto di non eseguire la verifica del tipo su un parametro e permetterà di utilizzare la sintassi fortemente tipizzata nel codice del plugin. La risoluzione del nome delle proprietà avverrà in fase di esecuzione e se un oggetto passato non ha tali proprietà verrà generata un'eccezione.

var o = new { FirstName = "John", LastName = "Doe" }; 

var result = MyMethod(o); 

string MyMethod(dynamic o) 
{ 
    return o.FirstName + " " + o.LastName; 
} 

Read more in this blog post

7

Questo esempio converte oggetto anonimo a un dizionario:

IDictionary<string, object> AnonymousObjectToDictionary(object propertyBag) 
{ 
    var result = new Dictionary<string, object>(); 
    if (propertyBag != null) 
    { 
     foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(propertyBag)) 
     { 
      result.Add(property.Name, property.GetValue(propertyBag)); 
     } 
    } 
    return result; 
} 

si può chiamare in questo modo:

AnonymousObjectToDictionary(new { foo = 11, bar = "Be happy" }); 
3

Se si tratta di un tipo anonomous da LINQ, quindi puoi farlo facilmente passando IEnumerable.

Ecco un esempio di un metodo di ricezione

public static void MyMethod<IEnumType>(ref IEnumerable<IEnumType> ienum) 
    { 
     using (DataTable dt = new DataTable()) 
     { 
      ienum.First(ie => true).GetType().GetProperties().ToList().ForEach(pr => dt.Columns.Add(pr.Name, typeof(string))); //Parallelization not possible since DataTable columns must be in certain order 

      ienum.ToList().ForEach(ie => //Parallelization not possible since DataTable rows not synchronized. 
       { 
        List<object> objAdd = new List<object>(); 
        ie.GetType().GetProperties().ToList().ForEach(pr => objAdd.Add(ie.GetType().InvokeMember(pr.Name, BindingFlags.GetProperty, null, ie, null))); //Parallelization not possible since DataTable columns must be in certain order 
        dt.Rows.Add(objAdd.ToArray()); 
        objAdd.Clear(); 
        objAdd = null; 
       }); 
      //Do something fun with dt 
     } 
    } 

Naturalmente, dal momento che si sta utilizzando la riflessione, allora è possibile vedere i problemi di prestazioni sul machiens più lenti o dove si hanno sia un grande IEnumerable o un sacco di immobili a T.

1
public static void ExAnonymousType() 
{ 
    var nguoi = new { Ten = "Vinh", Tuoi = 20 }; 
    Console.WriteLine(nguoi.Ten + " " + nguoi.Tuoi); 
    DoSomeThing(nguoi); 

} 

private static void DoSomeThing(object nguoi) 
{ 
    Console.WriteLine(nguoi.GetType().GetProperty("Ten").GetValue(nguoi,null)); 
} 
Problemi correlati