首页 >Unity3D频道 >【Unity杂文】 > Unity3D研究院之C#数据绑定
2021
02-28

Unity3D研究院之C#数据绑定

最近研究了一段时间MMVM的数据绑定,目前得出的结论单向绑定就已经够使用了,目前有2种方法实现数据绑定。

方法1 使用一个类代替基础数据

如下代码所示,我使用int32来代替int 数据,这样就可以监听数据绑定了。由于C#并没有提供”=”符号的运算符重载,所以需要.value=xx 来触发数据变化事件,而其它计算,比较等都可使用运算符重载或隐式转换的方式。

        int32 i32 = new int32(100);

        //与各类型进行隐式转换
        int ii = i32 + 100*2000;
        float ff = i32;
        double dd = i32;

        //数据判断
        if (i32 == 10) { }
        if (i32 <= 10) { }

        //监听数据变化
        i32.valueChange += (change) => {
            Debug.Log(change);
        };

        //最终设置数值
        i32.value = 200;

        Debug.Log(i32);

 

int32.cs核心代码

using System;

public struct int32 : IComparable, IFormattable, IComparable<int32>, IEquatable<int32>
{
    private int m_Value;
    public int value
    {
        get { return m_Value; }
        set
        {
            if (m_Value != value)
            {
                m_Value = value;
                valueChange?.Invoke(m_Value);
            }
        }
    }
    public Action<int> valueChange;
    public int32(int value)
    {
        valueChange = null;
        m_Value = value;
    }
    public static explicit operator byte(int32 value)
    {
        return (byte)value.value;
    }
    public static explicit operator sbyte(int32 value)
    {
        return (sbyte)value.value;
    }
    public static explicit operator short(int32 value)
    {
        return (short)value.value;
    }
    public static explicit operator ushort(int32 value)
    {
        return (ushort)value.value;
    }
    public static implicit operator int(int32 value)
    {
        return value.value;
    }
    public static explicit operator uint(int32 value)
    {
        return (uint)value.value;
    }
    public static implicit operator long(int32 value)
    {
        return value.value;
    }
    public static explicit operator ulong(int32 value)
    {
        return (ulong)value.value;
    }
    public static implicit operator float(int32 value)
    {
        return value.value;
    }
    public static implicit operator double(int32 value)
    {
        return value.value;
    }
    public static bool operator <(int32 lhs, int32 rhs)
    {
        return lhs.m_Value < rhs.m_Value;
    }
    public static bool operator <=(int32 lhs, int32 rhs)
    {
        return lhs.m_Value <= rhs.m_Value;
    }
    public static bool operator >(int32 lhs, int32 rhs)
    {
        return lhs.m_Value > rhs.m_Value;
    }
    public static bool operator >=(int32 lhs, int32 rhs)
    {
        return lhs.m_Value >= rhs.m_Value;
    }
    public int CompareTo(object obj)
    {
        return CompareTo(Convert.ToInt32(obj));
    }
    public override string ToString()
    {
        return m_Value.ToString();
    }
    public string ToString(string format)
    {
        return m_Value.ToString(format);
    }
    public string ToString(IFormatProvider provider)
    {
        return m_Value.ToString(provider);
    }
    public string ToString(string format, IFormatProvider provider)
    {
        return m_Value.ToString(format, provider);
    }
    public int CompareTo(int32 other)
    {
        if (m_Value == other.m_Value)
        {
            return 0;
        }
        else if (m_Value > other.m_Value)
        {
            return 1;
        }
        return -1;
    }
    public bool Equals(int32 other)
    {
        return m_Value == other.m_Value;
    }
}

 

方法2 给基础数据起一个别名

利用nameof语法糖来代替别名,比如我们先有一个模块类。nameof语法糖会在编译的时候替换成字符串,所以它没有额外性能开销。

    public class ViewMod
    {
        static ViewMod mod;
        public static ViewMod Get()
        {
            if (mod == null)
                mod = new ViewMod();
            return mod;

        }

        public int m_Field;
        public int field
        {
            get { return m_Field; }
            set {
                //修改数据抛出事件
                m_Field = value;
                Event.Dispatch(nameof(field), value);
            }
        }
    }

监听模块类中数据被修改

        var mod = ViewMod.Get();

        //UI中监听变化
        Event.AddListener(nameof(mod.field), (i) =>
        {
            Debug.Log("change: " + i);
        });
        //模块修改
        mod.field = 200;

Event就比较简单了,注意这里还需要补充RemoveListener方法,如果要考虑其他short long float double这些数据可以拓展成泛型。

public class Event
{
    public static Dictionary<string, Action<int>> events = new Dictionary<string, Action<int>>();
    public static void Dispatch(string name, int value)
    {

        if (events.TryGetValue(name, out var action))
        {
            action?.Invoke(value);
        }
    }
    public static void AddListener(string name, Action<int> action)
    {
        if (!events.TryGetValue(name, out var __))
        {
            events[name] = action;
        }
        else
        {
            events[name] += action;
        }
    }
}

 

我个人觉得方法2好一些。

1.nameof绑定了数据与事件,这样改了一个另外一个必须要改,不会出现漏改的情况。

2.需要考虑重名的问题,可以拓展nameof进行拼接。

3.需要考虑List中数据的问题,可以拓展nameof进行拼接。

 

欢迎一起讨论MMVM在实际项目中的应用。

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之C#数据绑定》有 4 条评论

  1. Ben Liu说:

    这个应该是MVVM数据绑定吧

  2. Silversea说:

    MVVM的数据绑定以前前的uframe就做得还行了,就是效率差点

  3. JayW说:

    如果多个数据类 有相同名字的字段属性 是不是就不可以了

    • 雨松MOMO说:

      是的, 但是可以以模块类为基础获取类型做个拼接,一般同一个模块不会有两个相同字段属性,除非写了两个内部类,写两个一样的属性,但这种情况很少

留下一个回复

你的email不会被公开。 必填项已用 * 标注。