无法在 C# ConcurrentDictionary 上调用 PropertyChanged 事件
Unable to invoke PropertyChanged event on a C# ConcurrentDictionary
我正在尝试在 C#
中使用 ConcurrentDictionary
并在新项目添加到字典时处理事件,但我无法这样做,代码如下
public class TopicTaskConcurrentDictionary
{
#region Singleton
private static volatile ConcurrentDictionary<KeyValuePair<string, string>, IDataPipesService> _instance;
private static readonly object Sync = new object();
private static readonly object Lock = new object();
public static event EventHandler PropertyChanged;
public static ConcurrentDictionary<KeyValuePair<string, string>, IDataService> Instance
{
get
{
if (_instance != null) return _instance;
lock (Sync)
{
if (_instance == null)
{
_instance = new ConcurrentDictionary<KeyValuePair<string, string>, IDataService>();
}
}
return _instance;
}
}
#endregion Singleton
public void TryAdd(KeyValuePair<string, string> keyValuePair, IDataPipesService service) {
Instance.TryAdd(keyValuePair, service);
PropertyChanged(null, EventArgs.Empty);
}
}
这是我添加到字典中的方式
var dataService = _kernel.Get<IDataService>();
TopicTaskConcurrentDictionary.Instance.TryAdd(new KeyValuePair<string, string>(param.TagPrefix, param.TopicName), dataService);
向字典添加项目时,我希望调用 ProperyChanged
事件,如下所示
TopicTaskConcurrentDictionary.PropertyChanged += delegate (object o, EventArgs e)
{
foreach (var item in TopicTaskConcurrentDictionary.Instance) {
if (!item.Value.Running)
//do something
}
};
当我执行上述操作时,PropertyChanged
事件从未被调用,请问我哪里出错了?
您遇到封装问题。好像你错误地实现了单例模式。您正在将基础集合公开给客户端代码。这绕过了 TopicTaskConcurrentDictionary
逻辑。这就是为什么您的事件永远不会被引发的原因。您实际上是在调用 ConcurrentDictionary.TryAdd
而不是 TopicTaskConcurrentDictionary.TryAdd
.
您正在将新项目直接添加到基础集合中。
要修复您的代码,请删除 Instance
属性 或正确实施 Singleton。但是两个版本都应该至少实现 IEnumerable<T>
接口。
我将 PropertyChanged
替换为 CollectionChanged
(这在绑定场景中启用了 ObservableCollection
行为)并添加了 IEnumerable<T>.GetEnumerator()
的实现以使集合能够在 foreach
迭代。然后我正确地实现了单例模式。通过使用 Lazy<T>
,您可以显着减少创建单个共享线程安全实例的代码:
public sealed class TopicTaskConcurrentDictionary : IEnumerable, IEnumerable<KeyValuePair<KeyValuePair<string, string>, IDataPipesService>>
{
public static TopicTaskConcurrentDictionary Instance =>
TopicTaskConcurrentDictionary._instance.Value;
public event NotifyCollectionChangedEventHandler CollectionChanged;
private ConcurrentDictionary<KeyValuePair<string, string>, IDataPipesService> underlyingCollection;
private static readonly object Sync = new object();
private static readonly Lazy<TopicTaskConcurrentDictionary> _instance =
new Lazy<TopicTaskConcurrentDictionary>(() => new TopicTaskConcurrentDictionary());
private TopicTaskConcurrentDictionary()
{
this.underlyingCollection = new ConcurrentDictionary<KeyValuePair<string, string>, IDataPipesService>();
}
public void TryAdd(KeyValuePair<string, string> key, IDataPipesService value)
{
this.underlyingCollection.TryAdd(key, value);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new KeyValuePair<KeyValuePair<string, string>, IDataPipesService>(key, value)));
}
public IEnumerator<KeyValuePair<KeyValuePair<string, string>, IDataPipesService>> GetEnumerator()
{
foreach (KeyValuePair<KeyValuePair<string, string>, IDataPipesService> entry in this.underlyingCollection)
{
yield return entry;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
this.CollectionChanged?.Invoke(this, e);
}
}
基本上 _instance
必须引用 TopicTaskConcurrentDictionary
的实例而不是底层集合的实例(就像在原始实现中一样)。
示例:
TopicTaskConcurrentDictionary.Instance.CollectionChanged += delegate (object o, NotifyCollectionChangedEventArgs e)
{
foreach (KeyValuePair<KeyValuePair<string, string>, IDataPipesService> item in TopicTaskConcurrentDictionary.Instance)
{
;
}
};
var dataService = _kernel.Get<IDataService>();
TopicTaskConcurrentDictionary.Instance.TryAdd(new KeyValuePair<string, string>(param.TagPrefix, param.TopicName), dataService);
我正在尝试在 C#
中使用 ConcurrentDictionary
并在新项目添加到字典时处理事件,但我无法这样做,代码如下
public class TopicTaskConcurrentDictionary
{
#region Singleton
private static volatile ConcurrentDictionary<KeyValuePair<string, string>, IDataPipesService> _instance;
private static readonly object Sync = new object();
private static readonly object Lock = new object();
public static event EventHandler PropertyChanged;
public static ConcurrentDictionary<KeyValuePair<string, string>, IDataService> Instance
{
get
{
if (_instance != null) return _instance;
lock (Sync)
{
if (_instance == null)
{
_instance = new ConcurrentDictionary<KeyValuePair<string, string>, IDataService>();
}
}
return _instance;
}
}
#endregion Singleton
public void TryAdd(KeyValuePair<string, string> keyValuePair, IDataPipesService service) {
Instance.TryAdd(keyValuePair, service);
PropertyChanged(null, EventArgs.Empty);
}
}
这是我添加到字典中的方式
var dataService = _kernel.Get<IDataService>();
TopicTaskConcurrentDictionary.Instance.TryAdd(new KeyValuePair<string, string>(param.TagPrefix, param.TopicName), dataService);
向字典添加项目时,我希望调用 ProperyChanged
事件,如下所示
TopicTaskConcurrentDictionary.PropertyChanged += delegate (object o, EventArgs e)
{
foreach (var item in TopicTaskConcurrentDictionary.Instance) {
if (!item.Value.Running)
//do something
}
};
当我执行上述操作时,PropertyChanged
事件从未被调用,请问我哪里出错了?
您遇到封装问题。好像你错误地实现了单例模式。您正在将基础集合公开给客户端代码。这绕过了 TopicTaskConcurrentDictionary
逻辑。这就是为什么您的事件永远不会被引发的原因。您实际上是在调用 ConcurrentDictionary.TryAdd
而不是 TopicTaskConcurrentDictionary.TryAdd
.
您正在将新项目直接添加到基础集合中。
要修复您的代码,请删除 Instance
属性 或正确实施 Singleton。但是两个版本都应该至少实现 IEnumerable<T>
接口。
我将 PropertyChanged
替换为 CollectionChanged
(这在绑定场景中启用了 ObservableCollection
行为)并添加了 IEnumerable<T>.GetEnumerator()
的实现以使集合能够在 foreach
迭代。然后我正确地实现了单例模式。通过使用 Lazy<T>
,您可以显着减少创建单个共享线程安全实例的代码:
public sealed class TopicTaskConcurrentDictionary : IEnumerable, IEnumerable<KeyValuePair<KeyValuePair<string, string>, IDataPipesService>>
{
public static TopicTaskConcurrentDictionary Instance =>
TopicTaskConcurrentDictionary._instance.Value;
public event NotifyCollectionChangedEventHandler CollectionChanged;
private ConcurrentDictionary<KeyValuePair<string, string>, IDataPipesService> underlyingCollection;
private static readonly object Sync = new object();
private static readonly Lazy<TopicTaskConcurrentDictionary> _instance =
new Lazy<TopicTaskConcurrentDictionary>(() => new TopicTaskConcurrentDictionary());
private TopicTaskConcurrentDictionary()
{
this.underlyingCollection = new ConcurrentDictionary<KeyValuePair<string, string>, IDataPipesService>();
}
public void TryAdd(KeyValuePair<string, string> key, IDataPipesService value)
{
this.underlyingCollection.TryAdd(key, value);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new KeyValuePair<KeyValuePair<string, string>, IDataPipesService>(key, value)));
}
public IEnumerator<KeyValuePair<KeyValuePair<string, string>, IDataPipesService>> GetEnumerator()
{
foreach (KeyValuePair<KeyValuePair<string, string>, IDataPipesService> entry in this.underlyingCollection)
{
yield return entry;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
this.CollectionChanged?.Invoke(this, e);
}
}
基本上 _instance
必须引用 TopicTaskConcurrentDictionary
的实例而不是底层集合的实例(就像在原始实现中一样)。
示例:
TopicTaskConcurrentDictionary.Instance.CollectionChanged += delegate (object o, NotifyCollectionChangedEventArgs e)
{
foreach (KeyValuePair<KeyValuePair<string, string>, IDataPipesService> item in TopicTaskConcurrentDictionary.Instance)
{
;
}
};
var dataService = _kernel.Get<IDataService>();
TopicTaskConcurrentDictionary.Instance.TryAdd(new KeyValuePair<string, string>(param.TagPrefix, param.TopicName), dataService);