Non dimenticare mai questo principio: renderlo corretto, renderlo chiaro, renderlo conciso, farlo in fretta. In questo ordine. Quindi, primo codice l'attuazione ingenuo:
static IEnumerable<T> GetByIndex<T>(
List<T> list,
Func<T, TIndex> func,
TIndex key
) {
return list.Where(x => func(x) == key);
}
Usage:
List<Test> tests = new List<Test>() {
new Test { Name = "aaa", Value = 111, Valid = Valid.Yes },
new Test { Name = "aaa", Value = 111, Valid = Valid.Yes },
new Test { Name = "bbb", Value = 112, Valid = Valid.No },
new Test { Name = "bbb", Value = 111, Valid = Valid.No },
new Test { Name = "bbb", Value = 220, Valid = Valid.No },
new Test { Name = "ccc", Value = 220, Valid = Valid.Yes }
};
IEnumerable<Test> lookup = GetByIndex(tests, x => x.Name, "bbb");
Quanto sopra è corretto, chiaro e conciso. Quasi sicuramente è abbastanza veloce per i tuoi scopi.
Quindi, per quanto lo rende veloce è necessario prima misura:
- Stabilire criterio di rendimento ragionevole.
- Stabilire un banco di prova dei dati del mondo reale.
- Profilo l'approccio semplice contro il banco di prova dei dati del mondo reale. Si noti qui che la profilazione include la deduzione o meno di questa funzionalità come collo di bottiglia nell'applicazione.
Quindi, se e solo se questo non è abbastanza veloce, dovresti provare ad ottimizzare. Non sarebbe troppo difficile implementare uno IndexedList<T> : ICollection<T>
che consentirebbe di indicizzare varie proprietà.
Ecco un'implementazione ingenua che potrebbe iniziare:
class IndexedList<T> : IEnumerable<T> {
List<T> _list;
Dictionary<string, Dictionary<object, List<T>>> _dictionary;
Dictionary<string, Func<T, object>> _propertyDictionary;
public IndexedList(IEnumerable<string> propertyNames) : this(propertyNames, new List<T>()) { }
public IndexedList(IEnumerable<string> propertyNames, IEnumerable<T> source) {
_list = new List<T>();
_dictionary = new Dictionary<string, Dictionary<object, List<T>>>();
_propertyDictionary = BuildPropertyDictionary(propertyNames);
foreach (var item in source) {
Add(item);
}
}
static Dictionary<string, Func<T, object>> BuildPropertyDictionary(IEnumerable<string> keys) {
var propertyDictionary = new Dictionary<string,Func<T,object>>();
foreach (string key in keys) {
ParameterExpression parameter = Expression.Parameter(typeof(T), "parameter");
Expression property = Expression.Property(parameter, key);
Expression converted = Expression.Convert(property, typeof(object));
Func<T, object> func = Expression.Lambda<Func<T, object>>(converted, parameter).Compile();
propertyDictionary.Add(key, func);
}
return propertyDictionary;
}
public void Add(T item) {
_list.Add(item);
foreach (var kvp in _propertyDictionary) {
object key = kvp.Value(item);
Dictionary<object, List<T>> propertyIndex;
if (!_dictionary.TryGetValue(kvp.Key, out propertyIndex)) {
propertyIndex = new Dictionary<object, List<T>>();
_dictionary.Add(kvp.Key, propertyIndex);
}
List<T> list;
if (!propertyIndex.TryGetValue(key, out list)) {
list = new List<T>();
propertyIndex.Add(key, list);
}
propertyIndex[key].Add(item);
}
}
public IEnumerable<T> GetByIndex<TIndex>(string propertyName, TIndex index) {
return _dictionary[propertyName][index];
}
public IEnumerator<T> GetEnumerator() {
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
Usage:
List<Test> tests = new List<Test>() {
new Test { Name = "aaa", Value = 111, Valid = Valid.Yes },
new Test { Name = "aaa", Value = 111, Valid = Valid.Yes },
new Test { Name = "bbb", Value = 112, Valid = Valid.No },
new Test { Name = "bbb", Value = 111, Valid = Valid.No },
new Test { Name = "bbb", Value = 220, Valid = Valid.No },
new Test { Name = "ccc", Value = 220, Valid = Valid.Yes }
};
// build an IndexedList<Text> indexed by Name and Value
IndexedList<Test> indexed = new IndexedList<Test>(new List<string>() { "Name", "Value" }, tests);
// lookup where Name == "bbb"
foreach (var result in indexed.GetByIndex("Name", "bbb")) {
Console.WriteLine(result.Value);
}
Ma vedi, la ragione non si fa questo a meno che l'implementazione ingenuo non è già veloce abbastanza è a causa della complessità aggiuntiva che hai appena aggiunto al tuo sistema. Hai appena aggiunto un nuovo codice da mantenere, un nuovo codice da testare e potresti non ottenere nulla se questo non è più veloce sui dati del mondo reale o non è un collo di bottiglia della tua applicazione.
Suoni promettenti; Lo darò un'occhiata ... – pbz
L'idea alla base di i4o è molto accurata e penso che dovrebbe essere integrata nel framework. Sfortunatamente, come è ora, è limitato a un semplice singolo dove condizione (cioè solo dove qualcosa = "valore", no && o ||). Per il mio caso è stato comunque sufficiente. Grazie. – pbz