• dotnet 写一个支持层层继承属性的对象


    我最近在造一个比 Excel 差得多的表格控件,其中一个需求是属性的继承。大家都知道,表格里面有单元格,单元格里面允许放文本,文本可以放多段文本。本文的主角就是文本段落的样式属性,包括文本字体字号颜色等等属性。文本段落的属性,如果没有特别设置,将使用单元格里面的文本样式属性。而如果单元格里面,没有特别指定此单元格使用特殊的文本样式,将会继承使用当前所在的行的文本样式。如果当前行没有特殊指定文本样式属性,那么将会使用文档的默认样式。文档默认样式将会根据是否有特殊指定而采用主题样式

    如此复杂的层层继承逻辑,如果每个属性都需要自己一层层去寻找,那代码量将会特别多。维护起来就想吃桌子

    为了保住桌子,咱来写一个支持层层继承属性的对象。如在当前层找不到某个属性,将会往上一层自动去找,一层层找。如果都找不到,将返回默认值

    以下是这个类的定义代码

        public class FlattenObject
        {
            /// <summary>
            /// 创建带继承的对象
            /// </summary>
            /// <param name="reserved"></param>
            public FlattenObject(FlattenObject? reserved = null)
            {
                Reserved = reserved;
            }
    
            private FlattenObject? Reserved { get; }
            private Dictionary<string, object> ValueDictionary { get; } = new Dictionary<string, object>();
    
            /// <summary>
            /// 设置属性值
            /// </summary>
            /// <param name="value"></param>
            /// <param name="propertyName"></param>
            protected void SetValue(object value, [CallerMemberName] string propertyName = null!)
            {
                ValueDictionary[propertyName] = value;
            }
    
            /// <summary>
            /// 获取属性值
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="propertyName"></param>
            /// <returns></returns>
            protected T? GetValue<T>([CallerMemberName] string propertyName = null!)
                => GetValue<T>(default!, propertyName);
    
            /// <summary>
            /// 获取属性值
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="defaultValue"></param>
            /// <param name="propertyName"></param>
            /// <returns></returns>
            protected T GetValue<T>(T defaultValue, [CallerMemberName] string propertyName = null!)
            {
                if (ValueDictionary.TryGetValue(propertyName, out var value))
                {
                    return (T) value;
                }
                else
                {
                    if (Reserved is not null)
                    {
                        return Reserved.GetValue<T>(defaultValue, propertyName);
                    }
                    else
                    {
                        return defaultValue;
                    }
                }
            }
        }
    

    通过 Reserved 属性表示的是当前层的上一层的对象,用于给当前层进行继承。因为每一层都包含了上一层的对象,因此从最下层就可以一层层自动找到属性的值

    继承当前类型,即可写出下面代码

            class FooFlattenObject : FlattenObject
            {
                public FooFlattenObject(FlattenObject reserved = null) : base(reserved)
                {
                }
    
                public string FontName
                {
                    set => SetValue(value);
                    get => GetValue<string>();
                }
    
                public int Count
                {
                    set => SetValue(value);
                    get => GetValue<int>();
                }
            }
    

    如上面代码,在各个属性的 set 和 get 都换成调用方法,而不需要定义字段

    下面来尝试写单元测试

                "给定可继承的对象,可以从继承的对象拿到属性值".Test(() =>
                {
                    var reserved = new FooFlattenObject()
                    {
                        FontName = "微软雅黑"
                    };
    
                    var fakeFlattenObject = new FooFlattenObject(reserved);
                    Assert.AreEqual("微软雅黑", fakeFlattenObject.FontName);
    
                    fakeFlattenObject.Count = 2;
                    Assert.AreEqual(2, fakeFlattenObject.Count);
                    Assert.AreEqual(0, reserved.Count);
                });
    

    可以看到在 reserved 对象里面设置了 FontName 的值,可以被 fakeFlattenObject 继承拿到,同时自动读取的代码对于上层业务来说几乎没有

    对 fakeFlattenObject 进行设置 Count 的值,不会影响到 reserved 对象

    通过此方法可以让存在层层继承逻辑的代码不需要大量重复。除了在表格上使用,也可以用在如解析 PPT 的形状内文本,如 PPT 的图片裁剪等需要继承属性的逻辑上

    上面的代码也存在不足,那就是对于结构体不友好,如 bool 或 int 等类型,都需要转换为 object 存放

  • 相关阅读:
    第一次的作业
    第02组 Alpha冲刺(2/4)
    团队项目选题报告
    第一次个人编程作业
    第二次结对编程作业
    胖子的故事(四)
    关于博客园的聚合问题
    blog是写给谁看的
    ASP.NET Forms 身份验证
    要努力了!
  • 原文地址:https://www.cnblogs.com/lindexi/p/15530525.html
Copyright © 2020-2023  润新知