次のような単純なオブジェクトを含む WinForms アプリを作成しています。
public class MyObject : INotifyPropertyChanged // for two-way data binding
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
private int _IndexValue;
public int IndexValue
{
get { return Value; }
set
{
if (value != Value)
{
Value = value;
RaisePropertyChanged();
}
}
}
private string _StringValue;
public string StringValue
{
get { return _StringValue; }
set
{
if (value != _StringValue)
{
_StringValue = value;
_Modified = true;
RaisePropertyChanged();
}
}
}
private bool _Modified;
public bool Modified
{
get { return _Modified; }
set
{
if (value != _Modified)
{
_Modified = value;
RaisePropertyChanged();
}
}
}
public MyObject(int indexValue)
{
IndexValue = indexValue;
StringValue = string.Empty;
Modified = false;
}
}
固定数 (100,000) のオブジェクトと BindingSource を含む BindingList があります。どちらも次のように定義されています。
BindingList<MyObject> myListOfObjects = new BindingList<MyObject>();
BindingSource bindingSourceForObjects = new BindingSource();
bindingSourceForObjects .DataSource = myListOfObjects;
最後に、DataGridView コントロールがあります。オブジェクトの StringValue プロパティを表示する単一の列 ("STRINGVALUECOLUMN") があり、先ほど述べた BindingSource にバインドされています。
dataGridViewMyObjects.DataSource = bindingSourceForObjects;
アプリケーションの開始時に、100,000 個のオブジェクトを myListOfObjects に追加します。DGV には 1 つの列しかなく、表示されるプロパティは string.Empty に初期化されているため、基本的に 100,000 の「空白」行を含む DGV があります。この時点で、ユーザーは行の編集を開始して文字列を入力できます。それらを任意の順序で編集する必要がないため、1 つの文字列を最初の行に、次の文字列を 17 行目に、次の文字列を 24581 行目に、というように配置できます。ユーザーは、テキスト ファイルから文字列をインポートしたい場合があります。 . オブジェクトの数は固定 (100,000) で、既存の文字列が既に入力されている場合と入力されていない場合があるため、新しい文字列を追加する前に、インポート プロセス中にいくつかのチェックを実行する必要があります。以下のコードでは、これらのチェックを削除しましたが、アプリケーションのパフォーマンスには影響していないようです。ただし、以下のコードを使用して何万もの文字列をインポートすると、非常に遅くなります (5 万行をインポートするのに 4 ~ 5 分かかります)。このコード ブロックの何かに絞り込みました。
// this code is inside the loop that reads each line from a file...
// does this string already exist?
int count = myListOfObjects.Count(i => i.StringValue == stringFromFile);
if (count > 0)
{
Debug.WriteLine("String already exists!"); // don't insert strings that already exist
}
else
{
// find the first object in myListOfObjects that has a .StringValue property == string.Empty and then update it with the string read from the file
MyObject myObject = myListOfObjects.FirstOrDefault(i => i.StringValue == string.Empty);
myObject.StringValue = stringFromFile;
}
基になるデータを更新して DGV コントロールに反映させることができるように、双方向バインディングが必要であることは理解していますが、INotifyPropertyChanged が時々遅くなる可能性があることも読みました。誰もこの問題に遭遇したことがありますか? もしそうなら、どのように解決しましたか?
--更新--
テスト目的で、次のものを置き換えました。
// does this string already exist?
int count = myListOfObjects.Count(i => i.StringValue == stringFromFile);
if (count > 0)
{
Debug.WriteLine("String already exists!"); // don't insert strings that already exist
}
else
{
// find the first object in myListOfObjects that has a .StringValue property == string.Empty and then update it with the string read from the file
MyObject myObject = myListOfObjects.FirstOrDefault(i => i.StringValue == string.Empty);
myObject.StringValue = stringFromFile;
}
以下を含む for ループを使用:
myListOfObjects[counter].StringValue = "some random string";
これは、100,000 オブジェクトでも非常に高速です。ただし、1) ファイルから読み取った文字列がリスト内のオブジェクトに割り当てられているかどうかを確認してから割り当てる前に、2) StringValue を持つリスト内の最初の使用可能なオブジェクトを見つけることができなくなりました。 property == string.Empty にして、それに応じてその値を更新します。したがって、次のようです。
int count = myListOfObjects.Count(i => i.StringValue == stringFromFile);
と
MyObject myObject = myListOfObjects.FirstOrDefault(i => i.StringValue == string.Empty);
...パフォーマンスの問題の原因です。BindingList に対してこれら 2 つの操作を実行する、より高速で効率的な方法はありますか?