0

ファイルシステムで動作するアプリケーションを書いています。アプリが最初に起動すると、後で (時間のかかる) 処理のために、要求されたファイルとフォルダーをメモリに読み込むクイック ルーチンが実行されます。(以下のコードを参照)。この時点で、処理されるファイルの数が表示されます。これは、進行状況バーを表示するために重要です。

カウントとファイル データを取得したら、後で処理するためにデータを保存する必要があります (たとえば、グローバル変数、プロパティ、またはクラスとして)。問題は、LINQを使用しているため、必然的に「var」として保存されていることです。変数を壊して調べると、かなり複雑な SelectQueryOperator と AnonymousType の組み合わせとして格納されています。

私が最初に考えたのは、先に進んでデータをループし、それを List<> として保存できる単純なデータに変換することでした (たとえば、ファイル名とパスを保存します)。処理する。いずれにせよ、処理を行うために後ですべてのデータをループする必要があり、ユーザーが最初にリストが作成されるのを待つことはできません。

このデータを保存して、最初に別のものに変換しなくても後でアクセスできるようにするにはどうすればよいですか?

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

問題は、LINQを使用しているため、必然的に「var」として格納されていることです。

いいえ、LINQにはを使用する必要があるものは何もありませんvar。基本的varに、メソッド内で強く型付けされた方法で匿名型を使用できます。

匿名タイプを名前付きタイプに変換するだけで、とまったく同じパフォーマンスが得られますvar。使用したときに見た違いは、クエリを評価ToListするまで、実際には何も実行されないということでした。ファイルシステムにまったくアクセスしていなかったのではないかと思います。(そもそもなぜクエリ式があるのか​​は明らかではありません。)Directory.EnumerateFiles

データを早期にロードする必要があるか、ロードしないかのどちらかです。質問からは明確ではありませんが、そのvar部分は完全に直交しています。

余談ですが、ファイルシステムで並列処理を使用することは、役立つというよりはむしろ妨げになる可能性があります。

于 2012-08-17T14:27:22.343 に答える
1

var非ローカル変数には使用できません。(これが理由です。)もしあなたが本当にあなたのコードを維持する必要がある人を本当に嫌うなら、それをobjectまたはdynamicとして保存し、として保存されている匿名タイプから情報を取得するためにいくつかの可能なハックの1つを使用することができます、objectしかしそれはおそらく良い考えではありません。

実際、最善の策は、aTextFileNameプロパティを使用して新しいタイプを作成し、匿名タイプではなくそれを使用することです。それはあなたのオプションの将来の開発者にとって最も単純で最も意味のないものです。

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

ワンラインワンダーを目指しても、通常は読みやすくはなりませんが、議論のためにオブジェクトの作成をすべて 1 か所にまとめるのに役立ちます。

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>

ただし、ここでロジックを確認するには、次の 2 つの方法があります。

  1. の動作はDirectory.EnumerateFiles、次を計算するために、特定の反復の値を知る必要があるようなものです。FindNextFile( Windows 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 に答える