我在应用程序的速度方面遇到了一些问题,并做了一些性能分析。结果表明,我的应用程序中有很多时间花在 linq 查询上,尤其是模型的 ID。我的想法是创建一个可观察的字典,其中ID作为键,模型作为值。这工作得很好,并且比 linq 查询快得多
.Any(x => x.ID == id)
或 linq 查询
.First(x => x.ID == id)
作为可观察词典,我使用了此示例
http://blogs.microsoft.co.il/shimmy/2010/12/26/observabledictionarylttkey-tvaluegt-c/
现在的问题是我需要创建一个可以绑定到我的视图的可观察集合。我试图使用可观察值属性扩展可观察字典,但这不起作用
public ObservableCollection<TValue> ObservableValues
{
get
{
if (observableValues == null)
{
lock (lockObject)
{
if (observableValues == null)
observableValues = new ObservableCollection<TValue>(Dictionary.Values);
}
}
return observableValues;
}
}
当我将模型添加到字典或更新模型时,绑定到视图的可观察集合将不会更新。
我能想到的最好的解决方案是在你的ObservableDictionnary
中保留一个包含字典所有值的ObservableCollection
。
然后,您必须更改类,以便在 dicionnary 中插入/更新/删除值时,它在ObservableCollection
中执行相同的操作,从而触发事件以更新视图。
感谢您的建议,但我尝试了一些 KeyedCollection 的实现,并决定将所有集合/字典切换到我的自定义 KeyedCollection 实现。性能与字典一样快,但不要使用KVP。这是我使用一些替换方法的可观察实现,它运行得非常快。
public class ObservableKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>, INotifyCollectionChanged
{
private const string CountString = "Count";
private readonly Func<TItem, TKey> _getKeyForItemDelegate;
// Constructor now requires a delegate to get the key from the item
public ObservableKeyedCollection(Func<TItem, TKey> getKeyForItemDelegate) : base()
{
if (getKeyForItemDelegate == null)
throw new ArgumentNullException("Delegate passed can't be null!");
_getKeyForItemDelegate = getKeyForItemDelegate;
}
protected override TKey GetKeyForItem(TItem item)
{
return _getKeyForItemDelegate(item);
}
/// <summary>
/// Method to add a new object to the collection, or to replace an existing one if there is
/// already an object with the same key in the collection.
/// </summary>
public void AddOrReplace(TItem newObject)
{
int i = GetItemIndex(newObject);
if (i != -1)
SetItem(i, newObject);
else
Add(newObject);
}
/// <summary>
/// Method to replace an existing object in the collection, i.e., an object with the same key.
/// An exception is thrown if there is no existing object with the same key.
/// </summary>
public void Replace(TItem newObject)
{
int i = GetItemIndex(newObject);
if (i != -1)
SetItem(i, newObject);
else
throw new Exception("Object to be replaced not found in collection.");
}
/// <summary>
/// Method to get the index into the List{} in the base collection for an item that may or may
/// not be in the collection. Returns -1 if not found.
/// </summary>
private int GetItemIndex(TItem itemToFind)
{
TKey keyToFind = GetKeyForItem(itemToFind);
if (this.Contains(keyToFind))
return this.IndexOf(this[keyToFind]);
else return -1;
}
// Overrides a lot of methods that can cause collection change
protected override void SetItem(int index, TItem item)
{
var oldItem = base[index];
base.SetItem(index, item);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, item, oldItem);
}
protected override void InsertItem(int index, TItem item)
{
base.InsertItem(index, item);
OnCollectionChanged(NotifyCollectionChangedAction.Add, item);
}
protected override void ClearItems()
{
base.ClearItems();
OnCollectionChanged();
}
protected override void RemoveItem(int index)
{
TItem item = this[index];
base.RemoveItem(index);
OnCollectionChanged(NotifyCollectionChangedAction.Remove, item);
}
private bool _deferNotifyCollectionChanged = false;
public void AddRange(IEnumerable<TItem> items)
{
_deferNotifyCollectionChanged = true;
foreach (var item in items)
Add(item);
_deferNotifyCollectionChanged = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (_deferNotifyCollectionChanged)
return;
if (CollectionChanged != null)
CollectionChanged(this, e);
}
#region INotifyCollectionChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void OnPropertyChanged()
{
OnPropertyChanged(CountString);
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void OnCollectionChanged()
{
if (_deferNotifyCollectionChanged)
return;
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, TItem changedItem)
{
if (_deferNotifyCollectionChanged)
return;
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, changedItem));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, TItem newItem, TItem oldItem)
{
if (_deferNotifyCollectionChanged)
return;
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems)
{
if (_deferNotifyCollectionChanged)
return;
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItems));
}
#endregion
}