LINQ遇到问题时,请在List< FileInfo> [英] Having trouble with LINQ left join on List<FileInfo>
问题描述
我有两个List<FileInfo>
列表,SourceFiles
和DestFiles
.我想构建一个LINQ查询,该查询将返回文件名在Source
中但不在Dest
中的项目的列表,即左联接.
I have two List<FileInfo>
lists, SourceFiles
and DestFiles
. I want to build a LINQ query that will return a list of the items whose filenames are in Source
but not in Dest
, i.e. a left join.
我为SourceFiles
设置的数据是:
folder1\a.txt
folder1\b.txt
folder1\c.txt
folder1\d.txt
DestFiles
是:
folder2\a.txt
folder2\b.txt
folder2\c.txt
因此查询应返回folder1\d.txt
.
按照 MSDN示例,我尝试使用LINQ语法:
Following the MSDN example, I've tried using LINQ syntax:
var queryX = from s in SourceFiles
join d in DestFiles
on s.Name equals d.Name
into SourceJoinDest
from joinRow in SourceJoinDest.DefaultIfEmpty()
select new
{
joinRow.FullName
};
并使用扩展方法:
var query = SourceFiles.GroupJoin(DestFiles,
source => source.Name,
dest => dest.Name,
(source,dest) => new
{
path = source.FullName
}).Select(x => x.path.DefaultIfEmpty())
但是这些都不起作用; LINQ语法版本返回Object reference not sent to an instance of an object
,扩展版本返回Enumeration yielded no results.
But neither one of these work; the LINQ syntax version returns Object reference not sent to an instance of an object
and the extension version returns Enumeration yielded no results.
我意识到这些查询仅返回FullName
属性的集合,而不返回完整的FileInfo
对象;我有接受每个FullName
并返回FileInfo
的代码,并针对查询中的每个项目执行此操作以重建列表.但是,如果有一种方法可以直接从查询中返回FileInfo
,那就太好了.
I realize that these queries are only returning sets of FullName
properties and not the full FileInfo
objects; I have code that takes each FullName
and returns a FileInfo
, and does this for each item in the query to rebuild the list. But if there's a way to return a FileInfo
directly from the query, that would be great.
推荐答案
我认为Join
不是这里的理想工具.基本上,您正在寻找Except
.内置的Except
没有通过lambda指定属性的重载.您将必须创建自己的IEqualityComparer
.您可以这样做,但是像这样:
I don't think Join
is the ideal tool here. Basically you're looking for an Except
. The built in Except
doesn't have the overload to specify your properties through lambda. You will have to create your own IEqualityComparer
. You could do it, however, like this:
var excepts = SourceFiles.Where(c => !DestFiles.Any(p => p.Name == c.Name)).ToList();
或者,要仅选择完整路径,可以在末尾使用Select
.
Or, to select just the full path, you can use Select
at the end.
var excepts = SourceFiles.Where(c => !DestFiles.Any(p => p.Name == c.Name))
.Select(f => f.FullName).ToList();
我建议使用扩展方法来快速执行Except
和Intersect
.
public static IEnumerable<U> Except<R, S, T, U>(this IEnumerable<R> mainList,
IEnumerable<S> toBeSubtractedList,
Func<R, T> mainListFunction,
Func<S, T> toBeSubtractedListFunction,
Func<R, U> resultSelector)
{
return EnumerateToCheck(mainList, toBeSubtractedList, mainListFunction,
toBeSubtractedListFunction, resultSelector, false);
}
static IEnumerable<U> EnumerateToCheck<R, S, T, U>(IEnumerable<R> mainList,
IEnumerable<S> secondaryList,
Func<R, T> mainListFunction,
Func<S, T> secondaryListFunction,
Func<R, U> resultSelector,
bool ifFound)
{
foreach (var r in mainList)
{
bool found = false;
foreach (var s in secondaryList)
{
if (object.Equals(mainListFunction(r), secondaryListFunction(s)))
{
found = true;
break;
}
}
if (found == ifFound)
yield return resultSelector(r);
}
//or may be just
//return mainList.Where(r => secondaryList.Any(s => object.Equals(mainListFunction(r), secondaryListFunction(s))) == ifFound)
// .Select(r => resultSelector(r));
//but I like the verbose way.. easier to debug..
}
public static IEnumerable<U> Intersect<R, S, T, U>(this IEnumerable<R> mainList,
IEnumerable<S> toIntersectList,
Func<R, T> mainListFunction,
Func<S, T> toIntersectListFunction,
Func<R, U> resultSelector)
{
return EnumerateToCheck(mainList, toIntersectList, mainListFunction,
toIntersectListFunction, resultSelector, true);
}
现在,您可以执行以下操作:
Now in your case you can do just:
var excepts = SourceFiles.Except(DestFiles, p => p.Name, p => p.Name, p => p.FullName)
.ToList();
这篇关于LINQ遇到问题时,请在List< FileInfo>的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!