C#监听List和Dictionary数据变化(修改清空等)

之前本职工作是 Unity 开发,经常需要监听列表或字典的数据变化来更新 UI。本文介绍两种监听方法,其中使用 new 修饰符的方法比较常用。
new 关键字
new
除了调用构造方法创建对象、泛型约束外,还可以作为修饰符使用,其作用是显式隐藏从基类继承的成员。
//To hide an inherited member, declare it in the derived class by using the same member name, and modify it with the new keyword. For example:
public class BaseC
{
public int x;
public void Invoke() { }
}
public class DerivedC : BaseC
{
new public void Invoke() { }
}
监听 List
方法1:new 修饰符
原理很简单,继承 List
,使用 new
隐藏 Add
等方法,并添加自己的委托回调。
namespace MyObservable
{
#region 公用委托(by梓喵出没博客)
public delegate void VoidValueCallback();
public delegate void OneValueCallback<T>(T data);
public delegate void TwoValueCallback<T1, T2>(T1 data1, T2 data2);
#endregion
public class ObserveList<T> : System.Collections.Generic.List<T>
{
public VoidValueCallback DOnClear;
public OneValueCallback<T> DOnAdd, DOnRemove;
public OneValueCallback<System.Collections.Generic.IEnumerable<T>> DOnAddRange;
#region 构造方法的重写(www.azimiao.com)
public ObserveList():base()
{
}
public ObserveList(System.Collections.Generic.IEnumerable<T> collection) : base(collection)
{
}
public ObserveList(int num) : base(num)
{
}
#endregion
/// <summary>
/// 注意此 removeall 并不是 clear,其调用参数为匹配规则
/// </summary>
public OneValueCallback<int> DOnRemoveAll;
public OneValueCallback<int> DOnRemoveAt;
public TwoValueCallback<int, int> DOnRemoveRange;
public new void Add(T item)
{
base.Add(item);
DOnAdd?.Invoke(item);
}
public new void AddRange(System.Collections.Generic.IEnumerable<T> collection)
{
base.AddRange(collection);
DOnAddRange?.Invoke(collection);
}
public new bool Remove(T item)
{
bool flag = base.Remove(item);
if (flag)
{
DOnRemove?.Invoke(item);
}
return flag;
}
public new void Clear()
{
base.Clear();
DOnClear?.Invoke();
}
public new int RemoveAll(Predicate<T> match)
{
int num = base.RemoveAll(match);
DOnRemoveAll?.Invoke(num);
return num;
}
public new void RemoveAt(int index)
{
base.RemoveAt(index);
DOnRemoveAt?.Invoke(index);
}
public new void RemoveRange(int index, int count)
{
base.RemoveRange(index, count);
DOnRemoveRange?.Invoke(index, count);
}
}
}
测试代码:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
MyObservable.ObserveList<int> test1 = new MyObservable.ObserveList<int>(0);
test1.DOnAdd += (int a) => { Console.WriteLine("add a number:" + a); };
test1.DOnRemove += (int a) => { Console.WriteLine("delete a number:" + a); };
test1.DOnClear += () =>{ Console.WriteLine("clear~"); };
test1.Add(100);
test1.Remove(100);
test1.Clear();
Console.ReadKey();
}
}
运行结果:
Hello World!
add a number:100
delete a number:100
clear~
方法2:实现 IList<T> 接口
一般来讲,该接口不是很常用,因为要实现一个能通过测试且符合标准的 List 新轮子大多是吃力不讨好的活,因此我只说下原理。
IList<T>
泛型接口是 ICollection<T>
泛型接口的后代,是所有泛型列表的基接口。我们继承该接口,说明当前这个类就是一个泛型列表。
继承该接口后,需要自己实现迭代器返回 IEnumerator
,复杂倒是谈不上,就是没必要。
当你实现该类后,将前文中的委托给它也来一份就行了。
监听 Dictionary(字典)
方法1:new 修饰符
和上文中的 List 大同小异,略。
方法2:实现 IDictionary<T>接口
原理同上文中的 IList<T>
,具体说明见:
https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.idictionary-2
当实现该接口后,添加自定义委托即可。