对BindingList执行LINQ的最快方法
本文关键字:方法 LINQ BindingList 执行 | 更新日期: 2023-09-27 17:58:37
我正在编写一个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;
}
}
我有一个BindingList,它将包含固定数量(100000)的对象以及BindingSource。这两者的定义都是这样的:
BindingList<MyObject> myListOfObjects = new BindingList<MyObject>();
BindingSource bindingSourceForObjects = new BindingSource();
bindingSourceForObjects .DataSource = myListOfObjects;
最后,我有了DataGridView控件。它有一列("STRINGVALUECOLUMN"),它显示我的对象的StringValue属性,并绑定到我刚才提到的BindingSource:
dataGridViewMyObjects.DataSource = bindingSourceForObjects;
当我的应用程序启动时,我会向myListOfObjects添加100000个对象。由于我的DGV中只有一列,并且它显示的属性已初始化为字符串。空的,我基本上有一个包含100000个"空白"行的DGV。此时,我的用户可以开始编辑行以输入字符串。他们不必按任何顺序编辑它们,所以他们可以将一个字符串放在第一行,将下一个字符串放置在第17行,将第24581行,等等。有时,我的用户会想从文本文件中导入字符串。由于我有固定数量的对象(100000),并且可能已经输入了一些现有字符串,也可能没有,所以在添加新字符串之前,我需要在导入过程中进行一些检查。在下面的代码中,我删除了这些检查,但它们似乎不会影响应用程序的性能。然而,如果我使用下面的代码导入数万个字符串,速度会非常慢(导入50k行需要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";
即使有100000个物体,这也非常快。但是,我现在已经失去了以下功能:1)在分配之前,检查从文件中读取的字符串是否已经分配给列表中的对象;2)在列表中找到StringValue属性==string的第一个可用对象。清空,然后相应地更新该值。看来:
int count = myListOfObjects.Count(i => i.StringValue == stringFromFile);
和
MyObject myObject = myListOfObjects.FirstOrDefault(i => i.StringValue == string.Empty);
是我表现问题的根源。有没有一种更快、更有效的方法可以对我的BindingList执行这两个操作?
关于Linq的问题是,它实际上只是标准循环,当然是优化的,但仍然是常规的旧循环,在后台代码中。
有一件事可能会加快你的代码是这样的:
myListOfObjects.Any(i => i.StringValue.Equals(stringFromFile));
这返回一个简单的布尔值,X是否存在。它会提前退出,所以如果没有必要的话,它不会扫描整个集合。.Count()
不仅需要扫描整个集合,还需要保持运行计数。
需要指出的另一点是,由于您使用的是FirstOrDefault
,这表明结果可能为null。在尝试使用myobject
之前,请确保对其进行了空检查。
最后,正如Saunders先生所建议的,检查事件堆栈,确保运行的代码不会比你想象的多。这在这样的操作中是危险的。您可能需要从初始化引擎中借用一些代码,并使用this.SuspendLayout()
和this.ResumeLayout()
问题可能是,当您更新底层数据时,会触发事件以导致网格更新。大量数据更改==大量更新。
我已经很久没有使用Windows窗体了,但请查看SuspendLayout
方法。