再学Blazor——组件建造者

使用 RenderTreeBuilder 创建组件是 Blazor 的一种高级方案。前几篇文中有这样创建组件的示例 builder.Component<MyComponent>().Build(); ,本文主要介绍该高级方案的具体实现,我们采用测试驱动开发(TDD)方法,大致思路如下:

  • 从测试示例入手
  • 扩展一个RenderTreeBuilder类的泛型扩展方法,泛型类型为组件类型
  • 创建组件建造者类(ComponentBuilder)提供方法来构建组件
  • 通过组件的属性选择器来设置组件参数
  • 构建时能返回组件的对象实例

1. 示例

首页我们从一个我们预想的高级方案示例入手,然后逐渐分析并实现我们预想的方案。下面是预想的示例代码:

class MyComponent : ComponentBase
{private MyTest test; //MyTest组件的对象实例//覆写构建呈现树方法protected override void BuildRenderTree(RenderTreeBuilder builder){builder.Component<MyTest>().Set(c => c.Title, "Hello")    //设置MyTest组件Title参数.Build(value => test = value); //建造组件并给MyTest实例赋值}
}

2. 扩展方法

下面实现builder.Component<MyTest>()这行代码,这是RenderTreeBuilder的一个扩展方法,该方法返回组件建造者类(ComponentBuilder)。

public static class Extension
{//泛型T是Blazor组件类型public static ComponentBuilder<T> Component<T>(this RenderTreeBuilder builder) where T : notnull, IComponent{//返回一个组件建造者类对象,将builder传递给建造者//其内部方法需要通过builder来构建组件return new ComponentBuilder<T>(builder);}
}

3. 建造者类

接下来实现组件建造者类(ComponentBuilder),该类是手动构建组件的核心代码,提供设置组件参数以及构建方法。

public class ComponentBuilder<T> where T : IComponent
{//手动构建呈现器private readonly RenderTreeBuilder builder;//组件参数字典,设置组件参数时,先存入字典,在构建时批量添加internal readonly Dictionary<string, object> Parameters = new(StringComparer.Ordinal);//构造函数internal ComponentBuilder(RenderTreeBuilder builder){this.builder = builder;}//添加组件参数方法,name为组件参数名称,value为组件参数值//提供Add方法可以添加非组件定义的属性,例如html属性public ComponentBuilder<T> Add(string name, object value){Parameters[name] = value; //将参数存入字典return this;              //返回this对象,可以流式操作}//设置组件参数方法,selector为组件参数属性选择器表达式,value为组件参数值//使用选择器有如下优点:// - 当组件属性名称更改时,可自动替换// - 通过表达式 c => c. 可以直接调出组件定义的属性,方便阅读// - 可通过TValue直接限定属性的类型,开发时即可编译检查public ComponentBuilder<T> Set<TValue>(Expression<Func<T, TValue>> selector, TValue value){var property = TypeHelper.Property(selector); //通过属性选择器表达式获取组件参数属性return Add(property.Name, value);             //添加组件参数}//组件构建方法,action为返回组件对象实例的委托,默认为空不返回实例public void Build(Action<T> action = null){builder.OpenComponent<T>(0); //开始附加组件if (Parameters.Count > 0)builder.AddMultipleAttributes(1, Parameters); //批量添加组件参数if (action != null)builder.AddComponentReferenceCapture(2, value => action.Invoke((T)value)); //返回组件对象实例builder.CloseComponent();   //结束附加组件}
}

4. 属性选择器

为什么要用属性选择器,组件建造者类中已经提到,下面介绍如何通过属性选择器表达式来获取组件类型的属性对象。

public class TypeHelper
{//通过属性选择器表达式来获取指定类型的属性public static PropertyInfo Property<T, TValue>(Expression<Func<T, TValue>> selector){if (selector is null)throw new ArgumentNullException(nameof(selector));if (selector.Body is not MemberExpression expression || expression.Member is not PropertyInfo propInfoCandidate)throw new ArgumentException($"The parameter selector '{selector}' does not resolve to a public property on the type '{typeof(T)}'.", nameof(selector));var type = typeof(T);var propertyInfo = propInfoCandidate.DeclaringType != type? type.GetProperty(propInfoCandidate.Name, propInfoCandidate.PropertyType): propInfoCandidate;if (propertyInfo is null)throw new ArgumentException($"The parameter selector '{selector}' does not resolve to a public property on the type '{typeof(T)}'.", nameof(selector));return propertyInfo;}
}

5. 总结

以上就是组件建造者的完整实现过程,代码不长,但这些功能足以完成手动构建Blazor组件的需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/173055.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java面试题10

1.MyBatis 中 #{}和 ${}的区别是什么&#xff1f; #{} 是预编译的占位符&#xff0c;MyBatis会将其转化为一个占位符参数&#xff0c;安全性较高&#xff0c;可以防止 SQL注入&#xff1b; ${} 是字符串替换&#xff0c;直接将内容替换到SQL语句中&#xff0c;不会进行参数处理…

【腾讯云 HAI域探秘】基于高性能应用服务器HAI部署的 ChatGLM2-6B模型,我开发了AI办公助手,公司行政小姐姐用了都说好!

目录 前言 一、腾讯云HAI介绍&#xff1a; 1、即插即用 轻松上手 2、横向对比 青出于蓝 3、多种高性能应用部署场景 二、腾讯云HAI一键部署并使用ChatGLM2-6B快速实现开发者所需的相关API服务 1、登录 高性能应用服务 HAI 控制台 2、点击 新建 选择 AI模型&#xff0c;…

【C/C++】如何不使用 sizeof 求数据类型占用的字节数

实现代码&#xff1a; #include <stdio.h>#define GET_TYPE_SIZE(TYPE) ((char *)(&TYPE 1) - (char *) & TYPE)int main(void) {char a a;short b 0;int c 0;long d 0;long long e 0;float f 0.0;double g 0.0;long double h 0.0;char* i NULL;print…

Docker 镜像使用

当运行容器时&#xff0c;使用的镜像如果在本地中不存在&#xff0c;docker 就会自动从 docker 镜像仓库中下载&#xff0c;默认是从 Docker Hub 公共镜像源下载。 创建镜像 列出镜像列表 我们可以使用 docker images 来列出本地主机上的镜像。 runoobrunoob:~$ docker imag…

如何在gitlab上使用hooks

参考链接&#xff1a;gitlab git hooks 1. Git Hook 介绍 与许多其他版本控制系统一样&#xff0c;Git 有一种方法可以在发生某些重要操作时&#xff0c;触发自定义脚本&#xff0c;即 Git Hook&#xff08;Git 钩子&#xff09;。 当我们初始化一个项目之后&#xff0c;.git…

Qt 样式表

QLabel&#xff0c;应用于Widget&#xff1a; .QLabel {background-color:pink; }.QLabel[warnlevel_1] {border:5px solid yellow; }.QLabel[warnlevel_2] {border:5px solid red; } QWidget{background-color:rgb(54,54,54); }QLineEdit{border: 1px solid #ABCDA0; /…

《golang设计模式》第三部分·行为型模式-08-状态模式(State)

文章目录 1. 概念1.1 作用1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概念 1.1 作用 状态&#xff08;State&#xff09;指状态对象&#xff0c;用于封装上下文对象的特定状态行为&#xff0c;使得上下文对象在内部状态改变时能够改变其自身的行为。 1.1 角色…

Android设计模式--桥接模式

闻正言&#xff0c;行正道&#xff0c;左右前后皆正人 一&#xff0c;定义 将抽象部分与实现部分分离&#xff0c;使它们都可以独立地进行变化 二&#xff0c;使用场景 从模式的定义中&#xff0c;我们大致可以了解到&#xff0c;这里的桥接的作用其实就是连接抽象部分与实现…

PHP 针对mysql 自动生成数据字典

PHP 针对mysql 自动生成数据字典 确保php 可以正常使用mysqli 扩展 这里还需要注意 数据库密码 如果密码中有特殊字符 如&#xff1a; 首先&#xff0c;我们需要了解MySQL中的特殊字符包括哪些。MySQL中的特殊字符主要包括以下几类&#xff1a; 1. 单引号&#xff08;&a…

HandlerThread

1.HandlerThread 首先它是Thread&#xff0c;继承自Thread public class HandlerThread extends Thread {} 2.与Thread不同的地方 在Thread的run方法里面 调用Looper.prepare()创建Looper调用Looper.loop()&#xff0c;可循环处理消息 public class HandlerThread extends…

嵌入式硬件电路·电平

目录 1. 电平的概念 1.1 高电平 1.2 低电平 2. 电平的使用场景 2.1 高电平使能 2.2 低电平使能 2.3 失能 1. 电平的概念 电平是指电信号电压的大小或高低状态。在数字电子学中&#xff0c;电平有两种状态&#xff0c;高电平和低电平&#xff0c;用来表示二进制中…

ChatGPT初体验:注册、API Key获取与ChatAPI调用详解

自从2022年10月&#xff0c;ChatGPT诞生以后&#xff0c;实际上已经改变了很多&#xff01;其火爆程度简直超乎想象&#xff0c;一周的时间用户过百万&#xff0c;两个月的时间用户过亿。 目前ChatGPT4已经把2023年4月以前的人类的知识都学习到了&#xff0c;在软件工程里面&am…

Matplotlib不规则子图_Python数据分析与可视化

除了网格子图&#xff0c;matplotlib还支持不规则的多行多列子图网格。 plt.GridSpec()对象本事不能直接创建一个图形&#xff0c;他只是 plt.subplot()命令可以识别的简易接口。 这里创建了一个带行列间距的23网格&#xff1a; grid plt.GridSpec(2, 3, wspace0.4, hspace0…

mac VScode 添加PHP debug

在VScode里面添加PHP Debug 插件,根据debug描述内容操作 1: 随意在index里面写个方法,然后用浏览器访问你的hello 方法,正常会进入下边的内容 class IndexController {public function index(){return 您好&#xff01;这是一个[api]示例应用;}public function hello() {phpin…

group by

引入 日常开发中&#xff0c;我们经常会使用到group by。你是否知道group by的工作原理呢&#xff1f;group by和having有什么区别呢&#xff1f;group by的优化思路是怎样的呢&#xff1f;使用group by有哪些需要注意的问题呢&#xff1f; 使用group by的简单例子group by 工…

java学习part12多态

99-面向对象(进阶)-面向对象的特征三&#xff1a;多态性_哔哩哔哩_bilibili 1.多态&#xff08;仅限方法&#xff09; 父类引用指向子类对象。 调用重写的方法&#xff0c;就会执行子类重写的方法。 编译看引用表面类型&#xff0c;执行看实际变量类型。 2.父子同名属性是否…

力扣226. 翻转二叉树

递归 思路&#xff1a; 从根开始递归遍历二叉树&#xff0c;叶节点开始翻转&#xff1b;如果遍历到的当前的 root 节点左右两棵子树已经翻转&#xff0c;交换左右子树即可&#xff1b; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeN…

数组与链表

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 除了HashMap&#xff0…

计算机服务器中了mallox勒索病毒如何处理,mallox勒索病毒解密文件恢复

科技技术的发展推动了企业的生产运营&#xff0c;网络技术的不断应用&#xff0c;极大地方便了企业日常生产生活&#xff0c;但网络毕竟是一把双刃剑&#xff0c;网络安全威胁一直存在&#xff0c;近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机…

耶鲁博弈论笔记

编辑记录&#xff1a; 1126&#xff1a;开个新坑&#xff0c;耶鲁大学的博弈论课程&#xff0c; 和专业相关不大&#xff0c;纯兴趣&#xff0c;尽量写好一点吧 1. 首先指出博弈论是一种研究策略形式的方法&#xff0c;对于经济学中&#xff0c;完全竞争市场只能被动接受均衡…