如何将c#中的一个文件集合与另一个文件集合进行比较?

如何将c#中的一个文件集合与另一个文件集合进行比较?,c#,fileinfo,directoryinfo,C#,Fileinfo,Directoryinfo,我刚刚在学习C#(已经摆弄了大约2天了),我决定,为了学习,我将重建一个用VB6制作的旧应用程序,用于同步文件(通常通过网络) 当我在VB 6中编写代码时,其工作原理大致如下: var sourceFiles = source.GetFiles(); var destFiles = dest.GetFiles(); var sourceFilesMissingInDestination = sourceFiles.Except(destFiles, new FileNameComparer()

我刚刚在学习C#(已经摆弄了大约2天了),我决定,为了学习,我将重建一个用VB6制作的旧应用程序,用于同步文件(通常通过网络)

当我在VB 6中编写代码时,其工作原理大致如下:

var sourceFiles = source.GetFiles();
var destFiles = dest.GetFiles();

var sourceFilesMissingInDestination = sourceFiles.Except(destFiles, new FileNameComparer());

foreach (var file in sourceFilesMissingInDestination)
{
    // Do whatever
}
  • 创建一个
    脚本.FileSystemObject
  • 为源和目标创建目录对象
  • 为源和目标创建文件列表对象
  • 迭代源对象,并检查它是否存在于目标中
    • 如果没有,创建它
    • 如果是,请检查源版本是否更新/更大,如果是,请覆盖其他版本
  • 到目前为止,我的情况如下:

    private bool syncFiles(string sourcePath, string destPath) {
        DirectoryInfo source = new DirectoryInfo(sourcePath);
        DirectoryInfo dest = new DirectoryInfo(destPath);
    
        if (!source.Exists) {
            LogLine("Source Folder Not Found!");
            return false;
        }
    
        if (!dest.Exists) {
            LogLine("Destination Folder Not Found!");
            return false;
        }
    
        FileInfo[] sourceFiles = source.GetFiles();
        FileInfo[] destFiles = dest.GetFiles();
    
        foreach (FileInfo file in sourceFiles) {
            // check exists on file
        }
    
        if (optRecursive.Checked) {
            foreach (DirectoryInfo subDir in source.GetDirectories()) {
                // create-if-not-exists destination subdirectory
                syncFiles(sourcePath + subDir.Name, destPath + subDir.Name);
            }
        }
        return true;
    }
    
    我读过一些示例,这些示例似乎提倡使用FileInfo或DirectoryInfo对象对“Exists”属性进行检查,但我特别想寻找一种方法来搜索现有的文件集合/列表,而不是对每个文件的文件系统进行实时检查,因为我将在网络上这样做,并且不断地返回数千个文件目录是很慢的

    提前感谢。

    使用
    GetFiles()
    方法只能获取确实存在的文件。它不构成不存在的随机文件。所以你所要做的就是检查它是否存在于其他列表中

    这样做可能会奏效:

    var sourceFiles = source.GetFiles();
    var destFiles = dest.GetFiles();
    
    foreach (var file in sourceFiles)
    {
        if(!destFiles.Any(x => x.Name == file.Name))
        {
            // Do whatever
        }
    }
    
    注意:在调用
    GetFiles()
    之后,您当然不能保证某些内容没有改变。例如,如果您稍后尝试复制某个文件,则该文件可能已被删除或重命名


    也许用这种方法或类似的方法可以做得更好。例如,类似这样的事情:

    var sourceFiles = source.GetFiles();
    var destFiles = dest.GetFiles();
    
    var sourceFilesMissingInDestination = sourceFiles.Except(destFiles, new FileNameComparer());
    
    foreach (var file in sourceFilesMissingInDestination)
    {
        // Do whatever
    }
    
    其中FileNameComparer的实现方式如下:

    public class FileNameComparer : IEqualityComparer<FileInfo>
    {
        public bool Equals(FileInfo x, FileInfo y)
        {
            return Equals(x.Name, y.Name);
        }
    
    
        public int GetHashCode(FileInfo obj)
        {
            return obj.Name.GetHashCode();
        }
    }     
    
    公共类FileNameComparer:IEqualityComparer
    {
    公共bool等于(FileInfo x,FileInfo y)
    {
    返回等于(x.Name,y.Name);
    }
    public int GetHashCode(FileInfo obj)
    {
    返回obj.Name.GetHashCode();
    }
    }     
    

    但未经测试:p

    一个小细节,而不是

     sourcePath + subDir.Name
    
    我会用

     System.IO.Path.Combine(sourcePath, subDir.Name)
    
    Path对文件名和文件夹名执行可靠的、独立于操作系统的操作

    我还注意到,
    optRecursive.Checked
    不知从哪里冒出来。作为一个良好的设计,将其作为一个参数:

    bool syncFiles(string sourcePath, string destPath, bool checkRecursive)
    

    由于您提到它可能用于大量文件,请注意.NET 4,它有一个IEnumerable替换项来替换GetFiles(),可以让您以流式方式处理此文件。

    对于子目录,也可以使用Path.Combine(sourcePath,subDir.Name)除了sourcePath+subDir.name之外,还可以看一下GetFileSystemInfos方法:至于您的注意:我理解,但这是一个风险,我会先加载获取文件列表的操作,这样我就不必对成千上万个文件进行单独的存在检查。非常感谢你的回答,我要去看看“Any”的语法!从DirectoryInfo获取所有目标文件的速度更快。不要在所有目标文件上都存在调用,但要检查文件打开调用是否成功,因为文件可以删除或重命名,就像斯维什解释的那样。@Martinho:这是个好主意。我正试图想出这样一个聪明的方法,但我的大脑却让我失望:p+1表示“既然你提到它可能用于大量文件,请注意.NET 4,它有一个IEnumerable代替GetFiles(),可以让你以流式方式处理它。”+1使用
    Path.Combine
    可以帮助您解决结尾斜杠存在而有时不存在的问题。这让生活很痛苦。我也喜欢使用
    Path.Combine
    进行这种连接,但在这种情况下,为什么不直接使用
    subDir.FullName