2009-06-01 7 views
16

Mi sembra di scrivere questo codice più e più volte e volevo vedere se c'era un modo migliore di farlo in modo più generico.Modo elegante per passare dall'elenco di oggetti al dizionario con due delle proprietà

comincio fuori con una lista di Foo oggetti

Foo[] foos = GenerateFoos(); 

Credo che voglio creare un dizionario in cui la chiave e il valore sono entrambe le proprietà di Foo

ad esempio:

Dictionary<string, string> fooDict = new Dictionary<string, string>(): 
foreach (Foo foo in foos) 
{ 
    fooDict[foo.Name] = foo.StreetAddress; 
} 

c'è comunque la possibilità di scrivere questo codice in modo generico in quanto sembra un modello base in cui esiste una matrice di oggetti, una proprietà chiave una proprietà valore e un dizionario.

Qualche suggerimento?

Sto usando CONTRO 2005 (C#, 2,0)

risposta

54

Con LINQ:

var fooDict = foos.ToDictionary(x=>x.Name,x=>x.StreetAddress); 

(e sì, fooDict è Dictionary<string, string>)


modifica per mostrare il dolore in VS2005:

Dictionary<string, string> fooDict = 
    Program.ToDictionary<Foo, string, string>(foos, 
     delegate(Foo foo) { return foo.Name; }, 
     delegate(Foo foo) { return foo.StreetAddress; }); 

dove si ha (in Program):

public static Dictionary<TKey, TValue> ToDictionary<TSource, TKey, TValue>(
    IEnumerable<TSource> items, 
    Converter<TSource, TKey> keySelector, 
    Converter<TSource, TValue> valueSelector) 
{ 
    Dictionary<TKey, TValue> result = new Dictionary<TKey, TValue>(); 
    foreach (TSource item in items) 
    { 
     result.Add(keySelector(item), valueSelector(item)); 
    } 
    return result; 
} 
+0

tutto ciò che funziona in VS2005? – leora

+0

Beh, potresti scrivere qualcosa di simile usando metodi anonimi e una classe di utilità, ma probabilmente sarebbe più lavoro del tuo codice esistente ... –

+0

Oh, è carino.Molto meglio del mio :( –

6

Se si utilizza Framework 3.5, è possibile utilizzare il ToDictionary estensione:

Dictionary<string, string> fooDict = foos.ToDictionary(f => f.Name, f => f.StreetAddress); 

Per framework 2.0, il codice è più o meno così semplice come può essere.

È possibile migliorare le prestazioni un po ', specificando la capacità per il dizionario al momento della creazione, in modo che non ha a che fare eventuali ridistribuzioni mentre si riempie:

Dictionary<string, string> fooDict = new Dictionary<string, string>(foos.Count): 
+0

Qualsiasi cosa funzioni in VS2005? – leora

2

Senza LINQ, no , non ci sono helper integrati per questo. Si potrebbe scrivere un però:

// I forget if you need this delegate definition -- this may be already defined in .NET 2.0 
public delegate R Func<T,R>(T obj); 
public static Dictionary<K,V> BuildDictionary<T,K,V>(IEnumerable<T> objs, Func<T,K> kf, Func<T,V> vf) 
{ 
    Dictionary<K,V> d = new Dictionary<K,V>(); 
    foreach (T obj in objs) 
    { 
     d[kf(obj)] = vf(obj); 
    } 
    return d; 
} 

Dictionary<string, string> fooDict = BuildDictionary(foos, new Func<Foo,string>(delegate(Foo foo) { return foo.Name; }), new Func<Foo,string>(delegate(Foo foo) { return foo.StreetAddress; })); 

Non sembra quasi elegante come le risposte LINQ-based, lo fa ...

+0

Avrei bisogno di controllare, ma potresti trovare che devi specificare i tipi generici. .. l'inferenza di tipo generico è diventata più forte in C# 3.0 (non era poi così eccezionale in C# 2.0/VS2005) –

+0

Sono abbastanza sicuro che l'inferenza di tipo generico era in 2.0, ma l'inferenza delegata non lo era - se lo era, è possibile rilasciare il "nuovo Func " roba –

+0

Naturalmente, mi sembra sempre di dimenticare cosa di questo materiale è in 2.0 - Ho solo lasciato che i reclami del compilatore fossero il mio dispositivo di memoria :) –

1

Ecco una soluzione che sia compatibile con .NET 2.0 che utilizza System.Web .UI.Databinder per fare la riflessione sul nome della proprietà - si perde il controllo del tipo in fase di compilazione.

 public static Dictionary<string, string> ToDictionary<T>(List<T> list, string keyName, string valueName) 
    { 
     Dictionary<string, string> outputDictionary = new Dictionary<string, string>(); 
     foreach (T item in list) 
     { 
      string key = Eval<T, string>(item, keyName); 
      string value = Eval<T, string>(item, valueName); 
      output[key] = value; 
     } 

     return outputDictionary; 
    } 

    public static TOut Eval<TIn, TOut>(TIn source, string propertyName) 
    { 
     object o = DataBinder.GetPropertyValue(source, propertyName); 
     if (o is TOut) 
      return (TOut)o; 

     return default(TOut); 
    } 

si sarebbe chiamata come segue:

Dictionary<string, string> fooDict = ToDictionary(foos, "Name", "StreetAddress"); 
Problemi correlati