轻松实现深度Clone | Source Generators方式

前言

在开发中,我们经常需要创建某个类型实例的副本。

常用的方式,是继承ICloneable接口,然后自行实现Clone(),这会耗费一定的开发时间;或者使用序列化/反序列化方式变相实现,但是性能不高。

现在,可以尝试用Source Generators实现。

实现思路

首先,需要Clone的类必须声明一个特定的CloneableAttribute,这样Source Generators才知道为谁实现Clone方法。

然后,Source Generators遍历该类型的所有属性,为其编写属性赋值代码。

如果属性本身也是Cloneable类型,那就调用属性对应类型的Clone方法,实现深度克隆。

具体代码

1.添加CloneableAttribute

向待编译项目加入CloneableAttribute代码:

const string cloneableAttributeText = @"using System;

namespace CloneableDemo
{
    public sealed class CloneableAttribute : Attribute
    {
        public CloneableAttribute()
        {
        }
    }
}
";

context.AddSource("CloneableAttribute", SourceText.From(cloneableAttributeText, Encoding.UTF8));

2.遍历CloneableAttribute声明类

找到声明了CloneableAttribute的所有类型:

var cloneableAttribute = compilation.GetTypeByMetadataName("CloneableDemo.CloneableAttribute");
foreach (var classSymbol in classSymbols)
{
    if (!classSymbol.TryGetAttribute(cloneableAttribute, out var attributes))
        continue;

    context.AddSource($"{classSymbol.Name}_clone.cs", SourceText.From(CreateCloneCode(classSymbol), Encoding.UTF8));
}

3.生成Clone代码

遍历属性,生成Clone方法:

private string CreateCloneableCode(INamedTypeSymbol classSymbol)
        {
            string namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
            var propertyNames = classSymbol.GetMembers().OfType<IPropertySymbol>();
            var codes = new StringBuilder();
            foreach (var propertyName in propertyNames)
            {
                if (isCloneable(propertyName))
                {
                    codes.AppendLine($@"                {propertyName} = obj.{propertyName}?.Clone(),");
                }
                else
                {
                    codes.AppendLine($@"                {propertyName} = obj.{propertyName},");
                }
            }

            return $@"using System.Collections.Generic;

namespace {namespaceName}
{{
    public static class {classSymbol.Name}Extentions
    {{
        public static {classSymbol.Name} Clone(this {classSymbol.Name} obj)
        {{
            return new {classSymbol.Name}
            {{
 {codes.ToString()}
            }};
        }}
    }}
}}";
        }

4.使用

现在,就可以在目标项目中使用Clone方法了:

[Cloneable]
public class Class1
{
    public string A { get; set; }
    public Class2 B { get; set; }
}

[Cloneable]
public class Class2
{
    public string A { get; set; }
}

var obj = new Class2()
{
    A = "My IO",
};
var deep = new Class1()
{
    A = "My IO",
    B = obj
};
var clone = deep.Clone();

结论

有了Source Generators,可以让编译器帮我们自动实现Clone方法,既节约了开发时间,又保证了性能!

如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值