Никогда не забывайте этот принцип: сделайте это правильно, проясните это, сделайте это кратким, сделайте это быстрым. В этой последовательности. Итак, сначала напишите наивную реализацию:
static IEnumerable<T> GetByIndex<T>(
List<T> list,
Func<T, TIndex> func,
TIndex key
) {
return list.Where(x => func(x) == key);
}
Использование:
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");
Вышеизложенное правильно, ясно и кратко. Почти наверняка это достаточно быстро для ваших целей.
Итак, чтобы сделать это быстро, вы должны сначала измерить:
- Установить разумный критерий эффективности.
- Создание испытательного стенда реальных данных.
- Профилируйте простой подход по отношению к испытательным стендам реальных данных. Обратите внимание, что профилирование включает определение того, является ли эта функция узким местом в вашем приложении.
Тогда, если и только если это не достаточно быстро для вас, вы должны попытаться оптимизировать. Было бы не сложно реализовать IndexedList<T> : ICollection<T>
, который позволял бы вам индексировать различные свойства.
Вот наивная реализация, с которой можно начать:
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();
}
}
Использование:
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);
}
Но, видите, причина, по которой вы этого не делаете, если наивная реализация еще недостаточно быстра, заключается в дополнительной сложности, которую вы только что добавили в свою систему. Вы только что добавили новый код для обслуживания, новый код для тестирования и, возможно, ничего не получите, если это не быстрее ваших реальных данных или не является узким местом вашего приложения.