2012-08-17 52 views
0

我正在编写一个与文件系统一起工作的应用程序。当应用程序第一次启动时,它会运行一个快速例程将所请求的文件和文件夹加载到内存中,以便以后(时间密集型)处理。 (请参阅下面的代码)。在这一点上,它给了我一个有多少文件将被处理的计数,这对于显示进度条是非常重要的。一旦我有了计数和文件数据,我需要存储数据以供以后处理(例如作为全局变量或属性或类)。问题在于,由于它使用LINQ,所以它被存储为“var”。当我断开并检查变量时,它将作为SelectQueryOperator和AnonymousType的相当复杂的组合存储。如何制作“var”全局或持久?

我的第一个想法是继续并循环访问数据并将其转换为简单的数据,我可以将其存储为列表<>(例如存储文件名和路径),但这样做只需几分钟 - 最多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 }; 
+1

这可能是花费时间的File.ReadAllText。你需要这样做吗?难道你不想仅仅存储文件名以待处理? – Polyfun 2012-08-17 14:29:42

+0

你甚至可能没有足够的内存来存储所有文件的全部文本。 – Servy 2012-08-17 14:30:30

回答

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 }; 

去为一个单行-wonder通常不会提高可读性,但它有助于将我们的对象创建全部放在o中为了讨论的缘故NE地点:

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

这是一个匿名T一个ParallelQuery<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) }; 

现在有没有从存储在ParallelQuery<NameAndContents>类型的字段阻止你。

你可能想在这里虽然检查的逻辑有两种方式:

  1. Directory.EnumerateFiles的运作是这样的,它需要知道,以计算下一个给定的迭代的价值。 (它基于FindNextFile Windows API函数)。这使得它在并行化方面很差。究竟有多少内在等待涉及ReadAllText余额,这很难预测。我不仅会针对非并行版本进行测试,而且会在做出任何更改之后重新进行测试,因为任何更改都会以新方式抛弃该平衡。

  2. 这里最大的问题就是ReadAllText。如果可以用一种更加按需的方式替代文本,那么它可能是一个巨大的胜利。

+0

谢谢(和其他人坦率地)一些非常好的信息。最重要的是,这是一个程序员(我)急于剪切和粘贴代码的问题,并且不了解实际编码的内容。我的目标是以最快的方式获得我必须使用的文件列表。然而,我并不需要ReadAllText,文件大小或其他任何东西,只是我可以稍后迭代的文件/路径列表,并从中获得计数,以便我可以显示进度。删除所有不必要的位使它超级快速,并给我留下一个不错的清单以供使用。 – 2012-08-17 17:56:35

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。

+0

我原则上同意这一点,但目前没有足够的关于托德要求的信息来建议此课程。除非你知道关于Todd的应用程序的更多信息,而不是在帖子中提到的信息。 – 2012-08-17 14:30:56

+0

我也是。在文件系统上并行处理的枚举器=在我的书中询问它。 – 2012-08-17 14:38:26

2

问题是,由于它使用LINQ,因此它被存储为“var”。

没有,也没什么可说是LINQ需要使用var。基本上var允许您在方法内以强类型的方式使用匿名类型。

您只需将您的匿名类型转换为指定类型,即可获得与var完全相同的性能。当你使用ToList时,你所看到的差异仅仅是,直到你评估查询,它不会实际做任何事情 - 我怀疑你根本没有访问文件系统。 (目前尚不清楚为什么你首先要查询表达式Directory.EnumerateFiles

要么你需要提前加载数据,要么你没有 - 从你的问题中不太清楚,但var部分是完全的正交于此。

另外,在文件系统上使用并行处理可能会阻碍而不是帮助。

1

对于任何非本地变量,您都不能使用var。 (This is why.)如果您确实讨厌需要维护代码的人,只需将其存储为objectdynamic,并使用其中一种可能的黑客手段从匿名类型中获取信息,该匿名类型已存储为object,但这可能不是一个好主意。

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

0

这有什么错

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

你真的需要实际读取每一个文件?

顺便说一句,VAR是不是动态类型,var是“编译器只是编译器的简写,在这里写右侧的类型我,所以我能避免像

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

任何时候你有码见“变种”,它可以与实际类型替换。

我还没有看到“进行AsParallel”如何应该在这里帮助。