我将向您展示一种非常灵活的不同方法(请参阅底部的 DotNetFiddle):您可以轻松编写自己的 LINQ 函数来扩展现有函数或者编写自己的函数并从 LINQ 查询的强大功能中受益。
在这个例子中,我正在改进 Linq 的 Distinct 函数,以便您可以指定一个用于分组的字段。
用法(示例):
var myQuery=(from x in Customers select x).MyDistinct(d => d.CustomerID);
在此示例中,查询按CustomerID 分组,并返回每个组的第一个元素。
MyDistinct的声明:
public static class Extensions
{
public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query,
Func<T, V> f)
{
return query.GroupBy(f).Select(x=>x.First());
}
}
可以看到,第二个参数f被声明为Func<T, V>,所以可以被.GroupBy语句使用。
回到你问题中的代码,如果你已经声明了
class MyObject
{
public string Name;
public string Code;
}
private MyObject[] _myObject = {
new MyObject() { Name = "Test1", Code = "T"},
new MyObject() { Name = "Test2", Code = "Q"},
new MyObject() { Name = "Test2", Code = "T"},
new MyObject() { Name = "Test5", Code = "Q"}
};
您可以将其与新定义的函数 MyDistinct 一起使用,如下所示:
var myQuery = (from x in _myObject select x).MyDistinct(d => d.Code);
将返回
名称 代码
测试1 T
测试2 Q
或者您可以在查询中使用.MyDistinct(d => d.Name),它会返回:
名称 代码
测试1 T
测试2 问
测试5 Q
请注意,由于MyDistinct 是使用泛型T 和V 声明的,因此它会自动识别和使用正确的对象类型并返回MyObject 元素。
高级用法
请注意,MyDistinct 始终采用每个组的第一个元素。如果你需要一个条件来定义你需要哪个元素呢?
你可以这样做:
public static class Extensions
{
public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query,
Func<T, V> f,
Func<IGrouping<V,T>,T> h=null)
{
if (h==null) h=(x => x.First());
return query.GroupBy(f).Select(h);
}
}
此修改允许您完全像以前一样使用它,即通过指定一个参数,如.MyDistinct(d => d.Name),但它也允许您指定一个有条件,如x => x.FirstOrDefault(y => y.Name.Contains("1")||y.Name.Contains("2")),作为第二个参数,如下所示:
var myQuery2 = (from x in _myObject select x).MyDistinct(d => d.Name,
x=>x.FirstOrDefault(y=>y.Name.Contains("1")||y.Name.Contains("2"))
);
如果你运行这个查询,结果是:
名称 代码
测试1 T
测试2 问
空
因为Test5 不满足条件(不包含 1 或 2),所以您在第三行得到 null。
注意:如果您只想公开条件,您可以更简单地实现它:
public static IEnumerable<T> MyDistinct2<T, V>(this IEnumerable<T> query,
Func<T, V> f,
Func<T,bool> h=null
)
{
if (h == null) h = (y => true);
return query.GroupBy(f).Select(x=>x.FirstOrDefault(h));
}
在这种情况下,查询将如下所示:
var myQuery3 = (from x in _myObject select x).MyDistinct2(d => d.Name,
y => y.Name.Contains("1") || y.Name.Contains("2")
);
所以你不需要写x=>x.FirstOrDefault(... condition ...)。
Try it in DotNetFiddle