我似乎找不到让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的魔力很新。这是正确的方法吗?你会如何重构这个?
答案 0 :(得分:4)
我首先要提取最明显的共性:
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();
......但是那时它是所以专家,我怀疑我是否会再次使用它......
答案 1 :(得分:3)
您可以传递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);
答案 2 :(得分:1)
我想将它重构为一个使用指定访问器的方法,或者可能传递一个委托,但我不知道它是如何工作的。
您可以使用对象语法并传递委托来执行此操作(假设您的.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),
// ...
}
答案 3 :(得分:1)
你可以这样做:
// "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)));
}