之前本职工作是 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

当实现该接口后,添加自定义委托即可。

梓喵出没博客(azimiao.com)版权所有,转载请注明链接:https://www.azimiao.com/7308.html
欢迎加入梓喵出没博客交流群:313732000

我来吐槽

*

*