我最近在造一个比 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 存放

dotnet 写一个支持层层继承属性的对象的更多相关文章

  1. 【C#】写一个支持多人聊天的TCP程序

    碎碎念 先谈谈我们要实现的效果:客户端可以选择要聊天的对象,或者直接广播消息(类似QQ的私聊和群消息) 那么,该如何实现呢? 首先明确的是,要分客户端和服务器端两个部分(废话) 客户端:选择要发送的对 ...

  2. [CSharp]传一个包含多个属性的对象,只改变其中个别属性值的方法

    需求 假如有这么一个需求,一个对象Person内的属性设置外包给了另外一个类Options, 而要设这个Person对象的属性,就必须传一个Options实例, 但又不能每个属性重新设一遍,只设要修改 ...

  3. 让我们纯手写一个js继承吧

    继承在前端逻辑操作中是比较常见的,今天我们就从零开始写一个js的继承方式 在es5中继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上Parent.call(this),在es6中则 ...

  4. 从零写一个Asp.net core手脚架(模型验证)

    一个asp.net core项目,一定包含了各种的实体,在RESTful api里面,有很多的参数传递,不建立实体则大量的参数需要自定验证正确性,并且Action上面会写的密密麻麻的参数 在asp.n ...

  5. python_way ,day11 线程,怎么写一个多线程?,队列,生产者消费者模型,线程锁,缓存(memcache,redis)

    python11 1.多线程原理 2.怎么写一个多线程? 3.队列 4.生产者消费者模型 5.线程锁 6.缓存 memcache redis 多线程原理 def f1(arg) print(arg) ...

  6. Java基础-继承-编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight。小车类Car是Vehicle的子类,其中包含的属性有载人数 loader。卡车类Truck是Car类的子类,其中包含的属性有载重量payload。每个 类都有构造方法和输出相关数据的方法。最后,写一个测试类来测试这些类的功 能。

    #29.编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight.小车类Car是Vehicle的子类,其中包含的属性有载人数 loader.卡车类T ...

  7. 继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类。 (2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法。 (3)子类中定义的成员变量和父类中定义的成员变量相同时,则父类中的成员变量不能被继承。 (4)子类中定义的成员方法,并且这个方法的名字返回类型,以及参数个数和类型与父类的某个成员方法完全相同,则父类的成员方法不能被继承。 分析以上程

    继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类. (2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法.(3)子类中定义的成员变量和父类中定义的 ...

  8. 写一个针对IQueryable<T>的扩展方法支持动态排序

    所谓的动态排序是指支持任意字段.任意升序降序的排序.我们希望在客户端按如下格式写: localhost:8000/api/items?sort=titlelocalhost:8000/api/item ...

  9. 类的继承与super()的意义以即如何写一个正确的异常类

    这些东西都是我看了许多名师课程和自己研究的成果,严禁转载,这里指出了如何正确的自己定义一个异常类并看一看sun写的java的源代码话题一:子类的构造器执行是否一定会伴随着父类的构造执行? 1.this ...

  10. 打算写一个《重学Node.js》系列,希望大家多多支持

    先放上链接吧,项目已经开始2周了:https://github.com/hellozhangran/happy-egg-server 想法 现在是2019年11月24日,还有人要开始学习Node.js ...

随机推荐

  1. .NET开源、免费、强大的交互式绘图库

    前言 今天大姚给大家分享一款.NET开源(采用MIT许可证).免费.强大的交互式绘图库,该库能够轻松地实现大型数据集的交互式显示.使用几行代码即可快速创建折线图.柱状图.饼图.散点图等不同类型的图表: ...

  2. 阿里二面:谈谈ThreadLocal的内存泄漏问题?问麻了。。。。

    引言 ThreadLocal在Java多线程编程中扮演着重要的角色,它提供了一种线程局部存储机制,允许每个线程拥有独立的变量副本,从而有效地避免了线程间的数据共享冲突.ThreadLocal的主要用途 ...

  3. base64格式上传图片后台写入

    前台 var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(e){//回调 } 后台 ...

  4. mybatis 中 if else 写法

    mybaits 中没有else要用chose when otherwise 代替 <choose> <when test=""> //... </wh ...

  5. C# 强大的网页处理类NSoup

    地址: https://github.com/GeReV/NSoup using System; using System.Collections.Generic; using System.Comp ...

  6. Java中关于优先队列PriorityQueue详解

    一.优先队列概述 优先队列PriorityQueue是Queue接口的实现,可以对其中元素进行排序, 可以放基本数据类型的包装类(如:Integer,Long等)或自定义的类 对于基本数据类型的包装器 ...

  7. java实战字符串+栈5:解码字符

    题目: 有形如 (重复字符串)<重复次数n> 的片段,解码后相当于n个重复字符串连续拼接在一起,求展开后的字符串.  求解: public static String zipString( ...

  8. PicGo图床配置github仓库上传typora图片

    (前提是已注册github并新建一个仓库作为你上传图片的位置) 首先在PicGo官网下载软件:https://picgo.github.io/PicGo-Doc/zh/ 打开typora,找到偏好设置 ...

  9. 实现一个简单的echarts词云图PythonFlask

    cloud.html 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta char ...

  10. 8 JavaScript函数

    8 JavaScript函数 在JS中声明函数和python差不多. 也要有一个关键字顶在前面. python是def, 到了JS里换成了function, 只不过在JS中没有像python那么死板, ...