2009-09-11 36 views
42

Ho avuto il seguente problema oggi e mi chiedevo se esiste una soluzione per il mio problema.Trasmissione a tipo anonimo

La mia idea era quella di costruire classi anonime e usarlo come un DataSource per una WinForm BindingSource:

public void Init() 
{ 
    var option1 = new 
        { 
         Id = TemplateAction.Update, 
         Option = "Update the Templates", 
         Description = "Bla bla 1." 
        }; 

    var option2 = new 
        { 
         Id = TemplateAction.Download, 
         Option = "Download the Templates", 
         Description = "Bla bla 2." 
        }; 

    var list = new[] {option1, option2}.ToList(); 

    bsOptions.DataSource = list; // my BindingSource 

    // cboTemplates is a ComboBox 
    cboTemplates.DataSource = bsOptions; 
    cboTemplates.ValueMember = "Id"; 
    cboTemplates.DisplayMember = "Option"; 

    lblInfoTemplates.DataBindings.Add("Text", bsOptions, "Description"); 
} 

che funziona bene finora.

Il problema che ho avuto è quello di ottenere Id fuori della proprietà "corrente" del BindingSource, perché non riesco a lanciare di nuovo al tipo anonimo:

private void cmdOK_Click(object sender, EventArgs e) 
{ 
    var option = (???)bsOptions.Current; 
} 

Credo che non v'è alcun modo per scoprire il tipo di "Corrente" e accedere alla proprietà "Id"? Forse qualcuno ha una buona soluzione ...

So che ci sono altri (e anche migliori) modi per ottenere l'Id (Riflessione, leggere il valore dal ComboBox, non usare tpyes anonimi, ...) I ' Sono solo cortese se è possibile ottenere il Tipo da bsOptions.Current in modo elegante.

+0

Br ... classe anonima può essere utile (a volte), ma in realtà, utilizzato in questo modo, per me è una regressione all'età VB: /. –

+1

Attendi solo che la dinamica entri in scena, sii contento che stiamo vedendo solo domande sul passaggio di oggetti anonimi. –

+1

Giusto, con abbastanza fortuna avremo un nuovo attributo "marquee" sull'etichetta: D –

risposta

69

Nota, come per il commento, vorrei solo far notare che anche io consiglio di usare un tipo reale quando è necessario passare intorno al programma come questo. I tipi anonimi dovrebbero essere usati solo localmente in un singolo metodo alla volta (secondo me), ma comunque, ecco il resto della mia risposta.


è possibile farlo utilizzando un trucco, ingannando il compilatore nel dedurre il tipo giusto per te:

using System; 

namespace ConsoleApplication4 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var a = new { Id = 1, Name = "Bob" }; 
      TestMethod(a); 

      Console.Out.WriteLine("Press enter to exit..."); 
      Console.In.ReadLine(); 
     } 

     private static void TestMethod(Object x) 
     { 
      // This is a dummy value, just to get 'a' to be of the right type 
      var a = new { Id = 0, Name = "" }; 
      a = Cast(a, x); 
      Console.Out.WriteLine(a.Id + ": " + a.Name); 
     } 

     private static T Cast<T>(T typeHolder, Object x) 
     { 
      // typeHolder above is just for compiler magic 
      // to infer the type to cast x to 
      return (T)x; 
     } 
    } 
} 

Il trucco è che all'interno del gruppo, lo stesso tipo anonimo (stesse proprietà, stesso ordine) si risolve nello stesso tipo, il che rende il trucco sopra funzionante.

private static T CastTo<T>(this Object value, T targetType) 
{ 
    // targetType above is just for compiler magic 
    // to infer the type to cast x to 
    return (T)x; 
} 

utilizzo:

var value = x.CastTo(a); 

Ma stiamo davvero spingendo i limiti qui. Usa un tipo reale, sembrerà anche più pulito.

+0

Non lo faccio in questo modo, tuttavia, poiché può essere soggetto a errori, è molto meglio creare semplicemente una classe per contenere i valori. –

+0

Sono d'accordo, anche se non è proprio così incline agli errori, ma sono d'accordo, qui è garantito un tipo reale, ho modificato la risposta per chiarire. –

+4

Secondo Mads Torgersen, il team C# si riferisce a questo trucco come "lanciato dall'esempio". Vedere il suo commento (il primo) su questo articolo: http://tomasp.net/blog/cannot-return-anonymous-type-from-method.aspx – LukeH

8

Per citare MSDN:

un tipo anonimo non può essere lanciato da qualsiasi interfaccia o il tipo ad eccezione di oggetto.

6

In C# 3.0, questo non è possibile. Dovrai attendere C# 4.0, che consente l'accesso alle proprietà in fase di esecuzione utilizzando variabili "dinamiche".

0

È possibile anche dichiarare un array di tipi anonimi che direttamente con la sintassi:

var data = new [] { 
    new {Id = 0, Name = "Foo"}, 
    new {Id = 42, Name = "Bar"}, 
}; 
1
public class MyExtensMethods{ 

    public static T GetPropertyValue<T>(this Object obj, string property) 
    { 
     return (T)obj.GetType().GetProperty(property).GetValue(obj, null); 
    } 
} 

class SomeClass 
{ 
    public int ID{get;set;} 
    public int FullName{get;set;} 
} 


// casts obj to type SomeClass 
public SomeClass CastToSomeClass(object obj) 
{ 
    return new SomeClass() 
    { 
     ID = obj.GetPropertyValue<int>("Id"), 
     FullName = obj.GetPropertyValue<string>("LastName") + ", " + obj.GetPropertyValue<string>("FirstName") 
    }; 
} 

... .quindi per eseguire il cast devi:

var a = new { Id = 1, FirstName = "Bob", LastName="Nam" }; 
SomeClass myNewVar = CastToSomeClass(a); 
+1

ma la bellezza dell'anonimo si perde qui ... devo scrivere SomeClasses ... – gsharp

+0

@gsharp. Il mio caso d'uso è per Unità testare un Jsonresult, che sta inviando un tipo anonimo di più classi realizzate. Quindi ho già SomeClasses ma volevo un tipo anonimo per passare JSON. Questo funziona perfettamente per me, grazie. –

15

Invece di eseguire il casting sul tipo personalizzato, provare a utilizzare il tipo dinamico.

vostro gestore di eventi sarebbe simile a questa:

private void cmdOK_Click(object sender, EventArgs e) 
{ 
    dynamic option = bsOptions.Current; 
    if (option.Id == 1) { doSomething(); } 
     else { doSomethingElse(); } 
} 
+2

+1: questa è una buona opzione in C# 4.0. opzione.Id sarà valutato in fase di esecuzione in questo caso. –

Problemi correlati