LINQ для объектов: есть ли способ передать в LINQ средство доступа, из которого можно получить значения? - PullRequest
2 голосов
/ 23 марта 2012

Я не могу найти способ заставить LINQ возвращать значение из указанного метода доступа. Мне известны имена методов доступа для каждого объекта, но я не уверен, возможно ли передать запрошенный метод доступа как переменную или иным образом добиться желаемого рефакторинга.

Рассмотрим следующий фрагмент кода:

// "value" is some object with accessors like: format, channels, language
row = new List<String> {
String.Join(innerSeparator, (from item in myObject.Audio
    orderby item.Key ascending
    select item.Value.format).ToArray()),
String.Join(innerSeparator, (from item in myObject.Audio
    orderby item.Key ascending
    select item.Value.channels).ToArray()),
String.Join(innerSeparator, (from item in myObject.Audio
    orderby item.Key ascending
    select item.Value.language).ToArray()),
// ...
}

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

string niceRefactor(myObj myObject, string /* or whatever type */ ____ACCESSOR) {
    return String.Join(innerSeparator, (from item in myObject.Audio
            orderby item.Key ascending
            select item.Value.____ACCESSOR).ToArray());
}

Я написал приличное количество C #, но все еще плохо знаком с магией LINQ. Это правильный подход? Как бы вы перестроили это?

Ответы [ 4 ]

4 голосов
/ 23 марта 2012

Я бы выделил наиболее очевидную общность для начала:

var audioItems = myObject.Audio.OrderBy(item => item.Key);
row = new List<String> {
    String.Join(innerSeparator, audioItems.Select(x => x.Value).ToArray());
    String.Join(innerSeparator, audioItems.Select(x => x.Format).ToArray());
    String.Join(innerSeparator, audioItems.Select(x => x.Channels).ToArray());
    String.Join(innerSeparator, audioItems.Select(x => x.Language).ToArray());
}

Если бы я использовал .NET 4, я бы тогда удалил вызовы ToArray, так как string.Join теперь имеет больше перегрузок:

var audioItems = myObject.Audio.OrderBy(item => item.Key);
row = new List<String> {
    String.Join(innerSeparator, audioItems.Select(x => x.Value));
    String.Join(innerSeparator, audioItems.Select(x => x.Format));
    String.Join(innerSeparator, audioItems.Select(x => x.Channels));
    String.Join(innerSeparator, audioItems.Select(x => x.Language));
}

Я может остановиться на этом.Но если вы хотите, вы всегда можете добавить другой метод расширения:

public static string Separate<T>(this IEnumerable<T> items, string separator)
{
    return string.Join(separator, items);
}

Тогда:

var audioItems = myObject.Audio.OrderBy(item => item.Key);
row = new List<String> {
    audioItems.Select(x => x.Value).Separate(innerSeparator));
    audioItems.Select(x => x.Format).Separate(innerSeparator));
    audioItems.Select(x => x.Channels).Separate(innerSeparator));
    audioItems.Select(x => x.Language).Separate(innerSeparator);
}

Я почти наверняка остановлюсь на этом.Вы могли бы продолжать:

public static IEnumerable<string> ProjectAndSeparateMany<T>(
    this IEnumerable<T> items, string separator, Func<T, object>... projections)
{
    return projections.Select(projection => items.Select(projection)
                                                 .Separate(separator);
}

И назвать это с помощью:

var audioItems = myObject.Audio.OrderBy(item => item.Key);
row = audioItems.ProjectAndSeparateMany(innerSeparator,
         x => x.Value, x => x.Format, x => x.Channels, x => x.Language).ToList();

... но в этот момент это так что специалистЯ сомневаюсь, что когда-нибудь буду использовать это снова ...

3 голосов
/ 23 марта 2012

Вы можете передать Func<AudioType, object>, чтобы выбрать желаемое свойство:

string niceRefactor(myObj myObject, Func<AudioType,object> propertySelector)
{
    return String.Join(innerSeparator, (from item in myObject.Audio
                                        orderby item.Key ascending
                                        select propertySelector(item.value)).ToArray());
}

Предполагается, что AudioType - это тип элементов значения, возвращаемых парами значений аудио-ключа.

Затем вы можете вызвать свой метод, например, как это:

string result = niceRefactor(myObject, x => x.format);
1 голос
/ 23 марта 2012

Вы можете сделать что-то подобное:

// "value" is some object with accessors like: format, channels, language
row = new List<String> {
    JoinProperties(myObject.Audio, innerSeparator, x => x.format),
    JoinProperties(myObject.Audio, innerSeparator, x => x.channels),
    JoinProperties(myObject.Audio, innerSeparator, x => x.language),
// ...
}


...


public string JoinProperties<TKey, TValue, TProperty>(IDictionary<TKey, TValue> dictionary, string separator, Func<TValue, TProperty> selector)
{
    return string.Join(separator, dictionary.OrderBy(kvp => kvp.Key).Select(kvp => selector(kvp.Value)));
}
1 голос
/ 23 марта 2012

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

Вы можете сделать это, используя синтаксис объекта и передавая делегат (предполагается, что ваш .Value имеет тип MyValueType):

string NiceRefactor(MyObj myObject, Func<MyValueType, string> accessor)
{
     return string.Join(innerSeparator, myObject.Audio.OrderBy(m => m.Key).Select(m => accessor(m.Value));
}

Используя это, вы можете написать:

// "value" is some object with accessors like: format, channels, language
row = new List<String> {
    NiceRefactor(myObject, v => v.format),
    NiceRefactor(myObject, v => v.channels),
    NiceRefactor(myObject, v => v.language),
    // ...
}
...