使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题

Metalama简介1. 不止是一个.NET跨平台的编译时AOP框架

Metalama简介2.利用Aspect在编译时进行消除重复代码

Metalama简介3.自定义.NET项目中的代码分析

Metalama简介4.使用Fabric操作项目或命名空间

Visual Studio中有提供快速操作(小灯泡)功能

以及重构(小刷子)功能

使用它们可以快速进行一些快捷的针对代码的操作,如提取接口、添加实现、自动属性、快速重构、删除引用等。

除官方提供的功能外我们还可以使用很多第三方插件来支持更多地功能。

Metalama可以通过编写代码的形式,让我们为指定的代码添加重构快速操作的功能。

自定义一个ToString的实时模板

很多图形编程或游戏编程中,我们会用到各种自定义类如矩阵、复数、坐标系等,为了方便Debug,我们通常会为这些类增加一个ToString方法的重写。

例如

internal class Program
{
private static void Main()
{
var point = new Point { X = 5, Y = 3};
Console.WriteLine($"point = {point}");
}
}
internal class Point
{
public double X;
public double Y;
public override string ToString()
{
return $"({X}, {Y})";
}
}

如果我们不想手写这个ToString方法,而想让VS直接为它生成。

则我们可以使用Metalama定义一个LiveTemplate,这样就可以在VS的工具中使用它了。

[LiveTemplate] // 表示当前Aspect为VS添加LiveTempate
internal class ToStringAttribute : TypeAspect
{
[Introduce(WhenExists = OverrideStrategy.Override, Name = "ToString")]
public string IntroducedToString()
{
var stringBuilder = new InterpolatedStringBuilder();
stringBuilder.AddText("{ ");
stringBuilder.AddText(meta.Target.Type.Name);
stringBuilder.AddText(" "); var fields = meta.Target.Type.FieldsAndProperties.Where(f => !f.IsStatic).ToList(); var i = meta.CompileTime(0); foreach (var field in fields)
{
if (i > 0)
{
stringBuilder.AddText(", ");
} stringBuilder.AddText(field.Name);
stringBuilder.AddText("=");
stringBuilder.AddExpression(field.Invokers.Final.GetValue(meta.This)); i++;
} stringBuilder.AddText(" }"); return stringBuilder.ToValue();
}
}

这样在,下列代码中使用重构功能,即可看到Metalama给的实时代码提示。

internal class Point
{
public double X;
public double Y;
}

使用Metalama添加一个VisualStudio的快速操作

我们最终的目的如下,对于标注了[Tostring]的类,增加一个将[ToString]切换至手动实现的功能点击后可实现自动添加一个ToString:

这需要我们在Aspect``ToStringAttribute中添加一个提示:

public class ToStringAttribute : TypeAspect
{
public override void BuildAspect(IAspectBuilder<INamedType> builder)
{
base.BuildAspect(builder);
// 添加一个建议手动实现的重构提示
if (builder.AspectInstance.Predecessors[0].Instance is IAttribute attribute)
{
builder.Diagnostics.Suggest(
new CodeFix("将 [ToString] 切换至手动实现", codeFixBuilder => this.ImplementManually(codeFixBuilder, builder.Target)),
builder.Target);
}
} /// <summary>
/// 当点击手动实现时的操作
/// </summary>
private async Task ImplementManually(ICodeActionBuilder builder, INamedType targetType)
{
await builder.ApplyAspectAsync(targetType, this);
await builder.RemoveAttributesAsync(targetType, typeof(ToStringAttribute));
} [Introduce(WhenExists = OverrideStrategy.Override, Name = "ToString")]
public string IntroducedToString()
{
// 获取非静态字段
var fields = meta.Target.Type.FieldsAndProperties.Where(f => !f.IsStatic).ToList(); // 构建一个$""字符串
var stringBuilder = new InterpolatedStringBuilder();
stringBuilder.AddText("{ ");
stringBuilder.AddText(meta.Target.Type.Name);
stringBuilder.AddText(" "); var i = meta.CompileTime(0); foreach (var field in fields)
{
if (i > 0)
{
stringBuilder.AddText(", ");
} stringBuilder.AddText(field.Name);
stringBuilder.AddText("=");
stringBuilder.AddExpression(field.Invokers.Final.GetValue(meta.This)); i++;
} stringBuilder.AddText(" }");
return stringBuilder.ToValue();
}
}

这样就可以对于已经添加了[ToString]的类实现以上功能

[ToString]
internal class Point // 在此处触发 Ctrl+.或右键
{
public double X;
public double Y;
}

引用

本章源代码:https://github.com/chsword/metalama-demo

Metalama官方文档: https://doc.metalama.net/

Metalama Nuget包: https://www.nuget.org/packages/Metalama.Framework/0.5.13-preview

Metalama简介5.配合VisualStudio自定义重构或快速操作功能的更多相关文章

  1. Metalama简介3.自定义.NET项目中的代码分析

    本系列其它文章 使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题 Metalama简介1. 不止是一个.NET跨平台的编译时AOP框架 Metalama简介2.利用Aspect在 ...

  2. Metalama简介4.使用Fabric操作项目或命名空间

    使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题 Metalama简介1. 不止是一个.NET跨平台的编译时AOP框架 Metalama简介2.利用Aspect在编译时进行消除重 ...

  3. Metalama简介2.利用Aspect在编译时进行消除重复代码

    上文介绍到Aspect是Metalama的核心概念,它本质上是一个编译时的AOP切片.下面我们就来系统说明一下Metalama中的Aspect. Metalama简介1. 不止是一个.NET跨平台的编 ...

  4. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(四):自定义T4模板快速生成页面

    前言:上篇介绍了下ko增删改查的封装,确实节省了大量的js代码.博主是一个喜欢偷懒的人,总觉得这些基础的增删改查效果能不能通过一个什么工具直接生成页面效果,啥代码都不用写了,那该多爽.于是研究了下T4 ...

  5. 3D触控简介:建立数字刻度应用及快速活动栏

    苹果公司通过 iPhone 6s 和 6s Plus 引入了与手机互动的全新方式:按压手势.你也许知道,苹果智能手表和苹果笔记本电脑早已具备这一功能,只是名称略有不同,为力感触控(Force Touc ...

  6. fragment、ListFragment使用ListView及自定义Listview等初始化操作

    fragment.ListFragment使用ListView及自定义Listview等初始化操作 1.先说一下 从官方api中说fragment碎片中使用Listview有专门的 ListView碎 ...

  7. VS code配置go语言开发环境之自定义快捷键及其对应操作

    VS code 配置 自定义快捷键 及其对应操作   由于 vs code 的官方 go 插件不支持像 goland 一样运行当前 go 文件, 只能项目 或者 package 级别地运行, 因此有必 ...

  8. 获取浏览器弹窗alert、自定义弹窗以及其操作

    web自动化测试第10步:获取浏览器弹窗alert.自定义弹窗以及其操作 - CSDN博客 http://blog.csdn.net/ccggaag/article/details/76573857 ...

  9. Phoenix简介概述,Phoenix的Java API 相关操作优秀案例

    Phoenix简介概述,Phoenix的Java API 相关操作优秀案例 一.Phoenix概述简介 二.Phoenix实例一:Java API操作 2.1 phoenix.properties 2 ...

随机推荐

  1. docker安装部署、fastDFS文件服务器搭建与springboot项目接口

    一.docker安装部署 1.更新yum包:sudo yum update 2.安装需要的软件包,yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动 ...

  2. memcached 能接受的 key 的最大长度是多少?

    key 的最大长度是 250 个字符.需要注意的是,250 是 memcached 服务器端内 部的限制,如果您使用的客户端支持"key 的前缀"或类似特性,那么 key(前缀 + ...

  3. Redis 集群会有写操作丢失吗?为什么?

    Redis 并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作.

  4. java-Map集合hei

    /* Map集合的特点: Map集合市一个双列集合,一个元素包含两个值:一个key,一个value Map集合中的元素,key和value的数据类型可以相同,也可以不同 Map集合中的元素,key不允 ...

  5. FPGA入门到精通系列1:数字电路基础知识

      本文主要介绍数字电路基础知识,用最简洁的内容介绍最核心的知识. 1.数字电路是什么? 数字电路是利用电源电压的高电平和低电平分别表示1和0,进而实现信息的表达.模拟信号:随时间连续变化的信号.处理 ...

  6. 自动驾驶运动规划-Dubins曲线

    1.Simple Car模型 如下图所示,Simple Car模型是一个表达车辆运动的简易模型.Simple Car模型将车辆看做平面上的刚体运动,刚体的原点位于车辆后轮的中心:x轴沿着车辆主轴方向, ...

  7. JDBC/Mybatis连接数据库报错:The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.

    造成这个的原因是maven导入MyBatis的时候会自动导入最新版本的8.0.11,然后8.0.11采用了新驱动,之前版本会报错. 当我们使用高版本的MySQL驱动时可以在获取数据库的连接getCon ...

  8. PAT B1024科学计数法

    科学计数法是科学家用来表示很大或很小的数字的一种方便的方法,其满足正则表达式 [+-][1-9].[0-9]+E[+-][0-9]+,即数字的整数部分只有 1 位,小数部分至少有 1 位,该数字及其指 ...

  9. YACS-2022.4-银组

    https://www.iai.sh.cn/contest 2022.04 银组,理论上 \(100+100+30+100\). T1 上锁的抽屉 题目描述 有一个抽屉柜里竖排了 \(n\) 只抽屉. ...

  10. Vue路由跳转时修改页面标题

    1 在main.js中添加如下代码 import Vue from 'vue' import App from './App.vue' import router from './router' // ...