如何使“var”全局或持久?

时间:2012-08-17 14:23:32

标签: c# filesystems var

我正在编写一个适用于文件系统的应用程序。当应用程序首次启动时,它会运行一个快速例程,将请求的文件和文件夹加载到内存中,以便以后(时间密集)处理。 (见下面的代码)。此时它会计算出要处理的文件数量,这对于显示进度条非常重要。

一旦我有了计数和文件数据,我需要存储数据以供以后处理(例如作为全局变量或属性或类)。问题在于它由于使用LINQ而被存储为“var”。当我破坏并检查变量时,它被存储为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 }; 

5 个答案:

答案 0 :(得分:2)

  

问题在于它因为使用LINQ而被存储为“var”。

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

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

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

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

答案 1 :(得分:1)

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

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

答案 2 :(得分: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) }; 

现在没有什么能阻止你将其存储在ParallelQuery<NameAndContents>类型的字段中。

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

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

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

答案 3 :(得分: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。

答案 4 :(得分:0)

有什么问题
        List<string> files = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories).ToList();

你真的需要真正阅读每个文件吗?

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

这样的代码
List<type> a = new List<type>()

每次看到“var”时,都可以用实际类型替换它。

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