0

我正在编写一个适用于文件系统的应用程序。当应用程序第一次启动时,它会运行一个快速例程将请求的文件和文件夹加载到内存中以供以后(时间密集型)处理。(见下面的代码)。在这一点上,它给了我将要处理多少文件的计数,这对于显示进度条很重要。

一旦我有了计数和文件数据,我需要存储数据以供以后处理(例如,作为全局变量或属性或类)。问题是它必须存储为“var”,因为它使用的是 LINQ。当我打破并检查变量时,它被存储为 SelectQueryOperator 和 AnonymousType 的相当复杂的组合。

我的第一个想法是继续遍历数据并将其转换为可以存储为 List<> 的简单数据(例如存储文件名和路径),但这样做实际上需要几分钟 - 最多 10 分钟或更长时间 - 到过程。无论如何,我稍后将不得不遍历所有这些数据以进行处理,而且我的用户不可能坐下来等待首先建立一个列表。

如何存储这些数据,以便以后可以访问它而不必先将其转换为其他东西?

var fileNames = 
from dir in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
select dir;

var fileContents = from file in fileNames.AsParallel() 
// Use AsOrdered to preserve source ordering 
let extension = Path.GetExtension(file)
let Text = File.ReadAllText(file)
select new { Text, FileName = file }; 
4

5 回答 5

2

问题是它必须存储为“var”,因为它使用的是 LINQ。

不,LINQ 没有任何要求您使用var. 基本上var允许您在方法中以强类型的方式使用匿名类型。

您需要做的就是将匿名类型转换为命名类型,您将获得使用var. 您在使用时看到的不同之处ToList在于,在您评估查询之前,它实际上不会做任何事情——我怀疑您根本没有访问文件系统。(不清楚为什么你首先有一个查询表达式Directory.EnumerateFiles。)

要么您需要尽早加载数据,要么您不需要 - 从您的问题中并不清楚,但该var部分与该部分完全正交。

顺便说一句,在文件系统上使用并行处理可能会阻碍而不是帮助。

于 2012-08-17T14:27:22.343 回答
1

您不能var用于任何非局部变量。(这就是原因。)如果您真的非常讨厌需要维护您的代码的人,您可以将其存储为objectordynamic并使用几种可能的黑客方法之一从已存储为的匿名类型中获取信息一个object,但这可能不是一个好主意。

真的,你最好的选择是创建一个具有TextFileName属性的新类型并使用它而不是匿名类型。对于您的选择的未来开发人员来说,这是最简单和最不重要的。

于 2012-08-17T14:27:25.657 回答
1

让我们稍微简化一下,并var在我们可以明确的地方明确说明..

var fileNames = 
from dir in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
select dir;

这与以下内容完全相同:

var fileNames = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories);

这与以下内容完全相同:

IEnumerable<string> fileNames = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)

现在为:

var fileContents = from file in fileNames.AsParallel() 
// Use AsOrdered to preserve source ordering 
let extension = Path.GetExtension(file)
let Text = File.ReadAllText(file)
select new { Text, FileName = file }; 

追求单行奇迹通常不会提高可读性,但为了便于讨论,它将有助于将我们的对象创建放在一个地方:

var fileContents = from file in fileNames.AsParallel() 
select new { Text = File.ReadAllText(file), FileName = Path.GetExtension(file) }; 

这是ParallelQuery<T>一个匿名的T。为了使我们可以存储它,我们需要停止使用匿名类:

private class NameAndContents
{
   public string Text{get;set;}
   public string FileName{get;set;}
}

ParallelQuery<NameAndContents> fileContents = from file in fileNames.AsParallel() 
select new NameAndContents{ Text = File.ReadAllText(file), FileName = Path.GetExtension(file) }; 

现在没有什么可以阻止您将其存储在 type 字段中ParallelQuery<NameAndContents>

您可能希望通过两种方式检查此处的逻辑:

  1. 的工作原理Directory.EnumerateFiles是,它需要知道给定迭代的值才能计算下一个迭代。(它基于FindNextFileWindows API 函数)。这使得它在并行化方面很差。ReadAllText很难预测平衡中涉及的内在等待有多少。我不仅会针对非并行版本对其进行测试,而且会在进行任何更改后重新测试,因为任何更改都会以新的方式打破这种平衡。

  2. 这里最大的打击是ReadAllText。如果完全有可能将其替换为以更按需的方式使用文本的东西,那么它可能是一个巨大的胜利。

于 2012-08-17T16:28:46.210 回答
0

预先加载所有数据并将其保留以供以后处理几乎总是错误的想法。你应该做的是一个一个地加载文件并在你去的时候处理它们,在这种情况下你不需要存储任何东西。

为了解决您的问题:您只需将操作结果投影到匿名类型以外的任何内容。例如,您可以创建一个类:

class FileData
{
    string FileName { get; set; }
    string Contents { get; set; }
}

var fileContents = from file in fileNames
                   select new FileData
                   {
                       FileName = Path.GetExtension(file),
                       Contents = File.ReadAllText(file)
                   }; 

只要您不在此变量上调用.ToList()或类似,您就可以即时枚举文件及其内容。

旁注:我删除了这个.AsParallel()调用,因为这个操作的瓶颈是文件系统,而不是 CPU。

于 2012-08-17T14:26:44.200 回答
0

有什么问题

        List<string> files = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories).ToList();

您真的需要实际阅读每个文件吗?

顺便说一句, var 不是动态类型, var 只是编译器的简写“编译器,在这里为我写右边的类型,这样我就可以避免像这样的代码

List<type> a = new List<type>()

任何时候你看到“var”,它都可以替换为实际类型。

我也看不出“AsParallel”在这里应该如何提供帮助。

于 2012-08-17T14:49:31.683 回答