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 存放
dotnet 写一个支持层层继承属性的对象的更多相关文章
- 【C#】写一个支持多人聊天的TCP程序
碎碎念 先谈谈我们要实现的效果:客户端可以选择要聊天的对象,或者直接广播消息(类似QQ的私聊和群消息) 那么,该如何实现呢? 首先明确的是,要分客户端和服务器端两个部分(废话) 客户端:选择要发送的对 ...
- [CSharp]传一个包含多个属性的对象,只改变其中个别属性值的方法
需求 假如有这么一个需求,一个对象Person内的属性设置外包给了另外一个类Options, 而要设这个Person对象的属性,就必须传一个Options实例, 但又不能每个属性重新设一遍,只设要修改 ...
- 让我们纯手写一个js继承吧
继承在前端逻辑操作中是比较常见的,今天我们就从零开始写一个js的继承方式 在es5中继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上Parent.call(this),在es6中则 ...
- 从零写一个Asp.net core手脚架(模型验证)
一个asp.net core项目,一定包含了各种的实体,在RESTful api里面,有很多的参数传递,不建立实体则大量的参数需要自定验证正确性,并且Action上面会写的密密麻麻的参数 在asp.n ...
- python_way ,day11 线程,怎么写一个多线程?,队列,生产者消费者模型,线程锁,缓存(memcache,redis)
python11 1.多线程原理 2.怎么写一个多线程? 3.队列 4.生产者消费者模型 5.线程锁 6.缓存 memcache redis 多线程原理 def f1(arg) print(arg) ...
- Java基础-继承-编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight。小车类Car是Vehicle的子类,其中包含的属性有载人数 loader。卡车类Truck是Car类的子类,其中包含的属性有载重量payload。每个 类都有构造方法和输出相关数据的方法。最后,写一个测试类来测试这些类的功 能。
#29.编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight.小车类Car是Vehicle的子类,其中包含的属性有载人数 loader.卡车类T ...
- 继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类。 (2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法。 (3)子类中定义的成员变量和父类中定义的成员变量相同时,则父类中的成员变量不能被继承。 (4)子类中定义的成员方法,并且这个方法的名字返回类型,以及参数个数和类型与父类的某个成员方法完全相同,则父类的成员方法不能被继承。 分析以上程
继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类. (2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法.(3)子类中定义的成员变量和父类中定义的 ...
- 写一个针对IQueryable<T>的扩展方法支持动态排序
所谓的动态排序是指支持任意字段.任意升序降序的排序.我们希望在客户端按如下格式写: localhost:8000/api/items?sort=titlelocalhost:8000/api/item ...
- 类的继承与super()的意义以即如何写一个正确的异常类
这些东西都是我看了许多名师课程和自己研究的成果,严禁转载,这里指出了如何正确的自己定义一个异常类并看一看sun写的java的源代码话题一:子类的构造器执行是否一定会伴随着父类的构造执行? 1.this ...
- 打算写一个《重学Node.js》系列,希望大家多多支持
先放上链接吧,项目已经开始2周了:https://github.com/hellozhangran/happy-egg-server 想法 现在是2019年11月24日,还有人要开始学习Node.js ...
随机推荐
- TP6框架--CRMEB学习笔记:布置后台管理框架+配置路由
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 最近在研究一个基于TP6的框架CRMEB,这里分享下我的开发心得 首先在上篇文章中,我们安装了CRMEBphp接口项目,需要可以看这一篇 ...
- 关于数据校验Bean Validation的学习
1,导相关依赖 2,常用的Validation注解 @NotNull: 标记字段不能为 null @NotEmpty: 标记集合字段不为空(至少要有一个元素) @NotBlank: 标记字段串字段不能 ...
- 基于quartus的FPGA学习系列
基于quartus学习 1.学习目标 quartus是altera的FPGA设计软件,用起来的感觉要比xilinx快.这里可以使用其完成各种基本的设计(就是不使用非必须IP核),一些基础的实验都可以在 ...
- KingbaseES V8R3 集群运维系列 -- sync_flag参数配置
案例说明: 在KingbaseES V8R3集群一主二备的架构中,配置了流复制为同步(sync)模式,但是集群启动后,流复制状态中显示备库是async模式(备库和主库数据已经同步),从备库的rec ...
- KingbaseES运维案例之---服务进程(backend process)终止
案例说明: 如下图所示:KingbaseES服务进程结构 KingbaseES使用客户端/服务器的模型. 对于每个客户端的连接,KingbaseES主进程接收到客户端连接后,会为其创建一个新的服务 ...
- SC的板子库~
观前须知 Sugar_Cube的博客园主页 声明 本文所有内容遵循CC BY-NC-SA 4.0 Deed原则 本文包含了笔者常用的OI算法.数据结构的模板 不保证算法最优,但能通过相应的模板题(如果 ...
- #容斥,莫比乌斯函数#洛谷 2231 [HNOI2002]跳蚤
题目 分析 考虑公约数为\(x\)很好做,就是\(\lfloor\frac{m}{x}\rfloor^n\), 这可以用枚举约数实现容斥当然也可以用莫比乌斯函数, 答案也就是\(\sum_{d|m}\ ...
- 深入理解HashMap和TreeMap的区别
目录 简介 HashMap和TreeMap本质区别 排序区别 Null值的区别 性能区别 共同点 深入理解HashMap和TreeMap的区别 简介 HashMap和TreeMap是Map家族中非常常 ...
- 一文总结ACE代码框架
一.前言 ACE_Engine框架是OpenAtom OpenHarmony(简称"OpenHarmony")的UI开发框架,为开发者提供在进行应用UI开发时所必需的各种组件,以及 ...
- HDC 2022精彩继续,多重亮点进来看!
原文:https://mp.weixin.qq.com/s/YX5vD4cxM8dA4v2ukFooyA,点击链接查看更多技术内容.