前言
在开发中,我们经常需要创建某个类型实例的副本。
常用的方式,是继承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“