diff --git a/HowToUseXericLibrary.md b/HowToUseXericLibrary.md index a914fae..d2fdd56 100644 --- a/HowToUseXericLibrary.md +++ b/HowToUseXericLibrary.md @@ -482,10 +482,6 @@ _blockBuilder = new TextBlockBuilder( TextBlockBuilder 是所有blocker的基类,使用它包裹所有文本将起到单纯拼接文本的作用,TextBlockBuilder里的三个block的作用是使用相应的富文本标签包裹,根据标签的类型,包裹器会自动决定标格式。 这里由于最外层没有要展现的标签,所以直接使用TextBlockBuilder作为最外层的拼接器,如果有最外层的标签,则可以直接使用这个标签的包裹器作为文本拼接器。 -`DelegateBlock`的作用是通过委托来实时获取一个变量,示例中直接获取value的文本值,拼接到结果中。 -TextBlockBuilder 可以不依靠如`DelegateBlock`这样的包裹器,可以直接将返回字符串类型的方法委传入,blocker 会自动识别; 前提是开启对应 blocker 里的`compatibility`兼容性检查,但这带来额外的性能开销,如果没有特别的需求,建议还是使用`DelegateBlock`进行多态封装,这样也会更全。 -在新的更新中,包裹器支持域写法,同时可以省略部分参数: - ``` // 这个包裹器也等同于: 重量1.23kg _blockBuilder = new TextBlockBuilder() @@ -502,8 +498,8 @@ _blockBuilder = new TextBlockBuilder() }; ``` -这里使用作用域的写法看起来更长了,但是对于较复杂的格式来说可以有更强的可读性,所以建议这样写。 -同时,还有更多的值包裹器: +这里使用作用域的写法相比入参写法看起来更长了,但是对于较复杂的格式来说可以有更强的可读性,所以建议这样写。 +同时,还有更多的值包裹器用法: * `SetParamters`: 在根包裹器上使用赋值方法,可以将一系列值一一对应地填入到后续一系列包裹器中。 * `DelegateBlock`: 委托包裹器允许声名一个返回值为字符串的委托来获取值。 @@ -511,17 +507,73 @@ _blockBuilder = new TextBlockBuilder() * `ValueBlock`: 值包裹器,搭配`SetParamters`来设置值,目标需要实现Tostring方法。 * `NumberValueBlock`: 数值类型包裹器,搭配`SetParamters`来设置值,设置值会自动限制在最最小范围内,如果给定的参数是字符串类型,将尝试通过默认的格式化方法转为数值。 -在实际使用时,只有`ValueBlock`默认会从`SetParamters`参数列表中获取值, -其他包裹器需要手动通过`SetParameterization`参数化包裹器,否则不会响应参数列表。 -值得时刻注意的是,`SetParamters`传递的值需要按深度优先的顺序逐一匹对,基本上可以认为是blockBuilde定义时的block先后顺序。 -如果`SetParamters`中参数数量少于需求,在匹配时将会提前退出,导致后续值保持不变; -如果遇到强制类型检查的block且值无法匹配,block不会处理这个值,而是丢给下一个,只有遇到这种情况时,引才有可能不是一一对应的形式。(默认认为是这么处理的,具体行为可以由block自己定义) +> 注意,所有BlockBuilder都支持直接使用反射获取对象,可以直接将返回字符串类型的方法名称传入,BlockBuilder 会自动识别; +> 前提是开启对应 blocker 的`compatibility`兼容性检查,但这带来额外的性能开销,如果没有特别的需求,建议还是使用`DelegateBlock`进行多态封装,这样也会更安全快速。 + +#### 参数列表 + +在任意`BlockBuilder`上使用`SetParamters()`方法可以传递一个参数列表,并以深度优先的方式传播(或者说以符合阅读习惯的方式传播),当遇到可以传入参数的`BlockBuilder`时,将会填入目前的参数并在内部的下标计数器上自增,以此类推。 + +在实际使用时,默认只有`ValueBlock`会从参数列表中获取值, +其他包裹器需要手动通过`.SetParameterization()`参数化包裹器,否则不会响应参数列表(这个方法是支持链式调用的)。 + +如果参数列表的数量少于需求,在匹配时将会提前退出,导致后续值保持不变; +而如果参数列表数量多于需求,在匹配时将不会关注这部分内容。 + +> 如果遇到需要强制类型检查的block且值无法匹配,block不会处理这个值,而是丢给下一个。 +> 只有遇到这种情况时,引用才有可能不是一一对应的形式,具体原因后面会讲。 -最后: * 关于体积:可以看到相比直接写富文本格式,这样写的体积更大。但对于开始不了解富文本格式的情况下,这个功能可以当作一种拼写检查。 * 关于安全:包裹器允许链式语法赋值,隐式地反向赋值,但部分模块也会使用反射功能,这可能会在部分平台上被认为是不安全的,并且可能不利于协作。 * 关于性能:这种做法可能比较浪费内存,使用时注意要声名在当前作用域的全局用作静态,或持实例唯一。 不过文本拼接不太需要担心,里面所有地方要拼接文本的地方都用了stringbuilder,比直接使用字符串内插可能还要高效。 +#### 自定义Block示例 + +``` +public class MyBlock : TextBlockBuilder // 继承自TextBlockBuilder +{ + // 声名一个构造函数并调用到基类,它可以传入一个参数列表,可以是任何对象,或是反射方法的名称。 + public MyBlock(params object[] content) : base(content) { } + + // 重载SetParamter方法,在被设置参数列表时调用,第一个参数是当前列表的下标,第二个参数是参数列表。 + internal override int SetParamter(int startIndex, object[] parameters) + { + // 父类提供parameterization字段用于判断当前blockBuilder是否应该处理参数 + if (parameterization) + { + // 使用parameters[startIndex]获取自己的参数,或者自行处理整个列表。 + // do something ... + startIndex++; // 用完自增一下下标。 + } + // 最后将参数列表向下传递 + foreach (var blocker in GetBlockChildren()) + { + startIndex = blocker.SetParamter(startIndex, parameters); + if (startIndex >= parameters.Length) + return startIndex; + } + return startIndex; + } + + // 重载转文本方法 + public override string ToString() + { + var sb = StartStringBuild(); // 这是拼接文本的对象池 + sb.Append("something start"); // 这里可以拼接开头 + AppendContents(sb); // 自动拼接子项 + sb.Append("something end"); // 这里可以拼接结尾 + return EndStringBuild(sb); // 释放并返回文本 + } + + // 重载文本转blockbuilder + public static bool TryParse(string context, out MyBlock blocker) + { + // context是一段包含任意标签的文本,也可能是多行的,需要自行处理为自己的block语法。 + // 如果成功,返回ture,否则false + } +} +``` + ### 文本转换: