2013-06-23 3 views
2

Я хотел бы сделать пользовательскую операцию над членами списка и иметь возможность указать, на каком свойстве я буду ее выполнять, но мне трудно найти правильный синтаксис для возврата результата в свойство.Выполнение пользовательской операции для каждого элемента списка <T>

Пример:

У меня есть список таких терминов, как показано ниже, и хотел бы, чтобы нормализовать их «Частота».

public class Term 
{ 
    public string Name { get; set; } 
    public double Frequency { get; set; } 
    public double Weight { get; set; } 
} 

Используя некоторый синтаксис, как это я должен быть в состоянии указать свойство я делаю операцию на:

List<Term> list = Normalize(artist.Terms, s => s.Frequency); 

(здесь это «Frequency» на «Term», но я должен быть в состоянии делать что-либо имущества любого типа, тип недвижимости всегда будет двойной)

Так вот что я созданный, но я не могу узнать, как выполнить операцию, ни присвоение результата к свойству:

private static List<T> Normalize<T>(List<T> elements, Func<T, double> func) 
{ 
    List<T> list = new List<T>(); 
    double fMin = elements.Min(func); 
    double fMax = elements.Max(func); 
    double fDelta = fMax - fMin; 
    double fInv = 1.0d/fDelta; 
    for (int i = 0; i < elements.Count; i++) 
    { 
     T t = elements[i]; 

     // What should I do from here ? 
     //double invoke = func.Invoke(term); 
     //term.Frequency = (term.Frequency - fMin) * fInv; 
    } 
    return list; 
} 

Как бы вы это достигли?

ответ

6

Если вы хотите, чтобы избежать использования выражения и отражения, вы можете просто обеспечить как getter функцию и setter функцию. Я также немного обновил метод, так как это было бы , изменяя объекты в оригинал список, поэтому я не думал, что было бы разумно создать и вернуть новый список; API будет обманчивым. (Вы можете легко переключить его обратно, если необходимо)

private static void Normalize<T>(List<T> elements, Func<T, double> getter, Action<T, double> setter) 
{ 
    double fMin = elements.Min(getter); 
    double fMax = elements.Max(getter); 
    double fDelta = fMax - fMin; 
    double fInv = 1.0d/fDelta; 
    for (int i = 0; i < elements.Count; i++) 
    { 
     T t = elements[i]; 

     double initialValue = getter(t); 
     double newValue = (initialValue - fMin) * fInv; 
     setter(t, newValue); 
    } 
} 

С использованием как:

Normalize(terms, t => t.Frequency, (t, normalizedFrequency) => t.Frequency = normalizedFrequency); 

И, конечно, это легко обновить, чтобы быть метод расширения поэтому он может быть использован как:

terms.Normalize(t => t.Frequency, (t, normalizedFrequency) => t.Frequency = normalizedFrequency); 

с этим изменением в Normalize подписи, это ясно, вы изменениясуществующих, а не создать новый. Со старой подписью, похоже, вы оставляете старый список и объекты нетронутыми, что не так.

+0

Отлично, спасибо. – Aybe

4

Вы можете использовать Expression, чтобы получить дескриптор объекта:

private static List<T> Normalize<T>(List<T> elements, Expression<Func<T, double>> func) 
{ 
    //... 
    var expr = (MemberExpression)func.Body; 
    var property = expr.Member as PropertyInfo; 
    if (property != null) 
     property.SetValue(/* element */, /* new value */); 
    //... 
} 

Кроме того, я бы рекомендовал использовать IEnumerable. Это является более гибким и может быть преобразовано в списки в любое время:

private static IEnumerable<T> Normalize(...) 
{ 
    foreach(...) 
    { 
     yield return ...; 
    } 
} 
+0

+1 для IEnumerable и выход. –

+1

Как работает первый? Я не вижу петли. –

+1

@Robert: Это просто пример того, как вы можете установить свойства. Я не хотел копировать весь код. –

Смежные вопросы