我最近在造一個比 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 存放
博客園博客只做備份,博客發布就不再更新,如果想看最新博客,請到 https://blog.lindexi.com/

本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可,歡迎轉載、使用、重新發布,但務必保留文章署名[林德熙](http://blog.csdn.net/lindexi_gd)(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用于商業目的,基于本文修改后的作品務必以相同的許可發布,如有任何疑問,請與我[聯系](mailto:[email protected]),
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/354386.html
標籤:.NET技术
