我将向您展示一种非常灵活的不同方法(请参阅底部的DotNetFiddle):您可以轻松编写自己的LINQ函数来扩展现有函数或编写自己的函数并从 LINQ 查询的强大功能中受益。
在这个例子中,我Distinct
以某种方式改进了 Linq 的功能,以便您可以指定一个用于分组的字段。
用法(示例):
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);
这将返回
名称 代码
Test1 T
Test2 Q
或者您可以.MyDistinct(d => d.Name)
在查询中使用,它返回:
名称 代码
Test1 T
Test2 Q
Test5 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"))
);
如果你运行这个查询,结果是:
名称 代码
Test1 T
Test2 Q
null
因为Test5
不满足条件(它不包含 1 或 2),所以您在第 3 行得到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 ...)
.
在 DotNetFiddle 中尝试