LINQ 到对象:有没有办法将要从中获取值的访问器传递给 LINQ

本文关键字:LINQ 获取 访问 对象 有没有 | 更新日期: 2023-09-27 18:28:54

我似乎找不到让 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 的魔力仍然很陌生。这是正确的方法吗?您将如何重构它?

LINQ 到对象:有没有办法将要从中获取值的访问器传递给 LINQ

我会从最明显的共性开始:

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();

。但在这一点上,它是如此专业,我怀疑我是否会再次使用它......

可以通过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);

我想将其重构为使用指定访问器的方法,或者可能传递委托,尽管我看不出它是如何工作的。

您可以使用对象语法并传递委托来执行此操作(这假设您的.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),
    // ...
}
你可以

做这样的事情:

// "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)));
}