记录一下初次使用动态编译的部分代码,最后会将反编译之后的完整代码贴在文章末尾.
需要用到的程序集/Nuget
因为是第一次使用动态编译,找资料时需要以下的两个包
System.CodeDom
Microsoft.CSharp
不过我的项目是基于 .NetCore 在最后编译成dll 时无法编译,后来使用以下两个包替换掉了Microsoft.CSharp
Microsoft.CodeAnalysis
Microsoft.CodeAnalysis.CSharp
所以此文主要的基于 .netcore 和 Microsoft.CodeAnalysis
CodeCompileUnit
官方定义: 为 CodeDOM 程序图形提供容器。
样例:
var gCompile = new CodeCompileUnit();
CodeNamespace
官方定义: 表示命名空间声明
样例:
var gNamespace = new CodeNamespace("Project.Import") `
//添加引用
gNamespace.Imports.Add(new CodeNamespaceImport("System.Text"));
//将此命名空间添加到容器中:
gCompile.Namespaces.Add(gNamespace);
CodeMemberMethod
官方定义: 表示某种类型的方法的声明
CodeTypeReference
官方定义: 表示对某类型的引用
CodeParameterDeclarationExpression
官方定义: 表示方法、属性或构造函数的参数声明
CodeSnippetStatement
官方定义 使用原义代码片段表示一条语句
样例:
// 声明一个方法
var method = new CodeMemberMethod()
{
Name = "InitCustomParam",
Attributes = MemberAttributes.Private | MemberAttributes.Final,
Parameters =
{
new CodeParameterDeclarationExpression(typeof(IEnumerable<string>), "fields")
},
ReturnType = new CodeTypeReference(typeof(string))
};
// 构建方法体, 方法体比较复杂, 这里采用的是 字符串拼接
var statement = new StringBuilder();
statement.AppendLine("var sb = new StringBuilder();");
statement.AppendLine("sb.Append(\"{\");");
statement.AppendLine("var _index = 0;");
statement.AppendLine("foreach (var item in fields)");
statement.AppendLine("{");
statement.AppendLine("var val = this.GetType().GetProperty(item).GetValue(this);");
statement.AppendLine("if(fields.Count()-1==_index)");
statement.AppendLine("sb.Append(string.Format(\"\\\"{0}\\\":\\\"{1}\\\"\", item, val));");
statement.AppendLine("else");
statement.AppendLine("sb.Append(string.Format(\"\\\"{0}\\\":\\\"{1}\\\",\", item, val));");
statement.AppendLine("_index++;");
statement.AppendLine("}");
statement.AppendLine("sb.Append(\"}\");");
statement.Append("return sb.ToString();");
method.Statements.Add(new CodeSnippetStatement(statement.ToString()));
// 将方法添加到 class 中
gClass.Members.Add(method);
CodeMemberField
官方定义: 表示某种类型的字段的声明
CodeArrayCreateExpression
官方定义: 表示创建数组的表达式
CodePrimitiveExpression
官方定义: 表示基元数据类型的值
// 声明一个字段
var gField4Param = new CodeMemberField(typeof(string[]), "_ParamFields");
gField4Param.Attributes = MemberAttributes.Private | MemberAttributes.Final;
// 默认值
gField4Param.InitExpression = new CodeArrayCreateExpression(typeof(string), properties.Select(p => new CodePrimitiveExpression(p)).Cast<CodeExpression>().ToArray());
// 将字段添加到 class 中
gClass.Members.Add(gField4Param);
CodeMemberProperty
官方定义: 表示某种类型的属性的声明
CodeMethodReturnStatement
官方定义: 表示返回值语句
CodeMethodInvokeExpression
官方定义: 表示调用方法的表达式
CodeThisReferenceExpression
官方定义: 表示对当前本地类实例的引用
CodeFieldReferenceExpression
官方定义: 表示对某字段的引用
CodeAttributeDeclaration
官方定义: 表示特性声明
// 声明一个属性
var gProperty4CustomizeParam = new CodeMemberProperty()
{
Name = "CustomizeParam",
Attributes = MemberAttributes.Public | MemberAttributes.Final,
// 类型
Type = new CodeTypeReference(typeof(string)),
// get 访问器
GetStatements =
{
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(new CodeThisReferenceExpression(),
method.Name,
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), gField4Param.Name)
))
},
// 添加特性
CustomAttributes = { new CodeAttributeDeclaration(new CodeTypeReference(typeof(IEIgnoreAttribute))) }
};
// 将属性添加到 class 中
gClass.Members.Add(gProperty4CustomizeParam);
CodeAssignStatement
官方定义: 表示简单的赋值语句
CodePropertySetValueReferenceExpression
官方定义: 表示属性集方法内的属性集方法调用的值参数
CodeAttributeArgument
官方定义: 表示在元数据特性声明中使用的参数
// 常规的属性字段声明
foreach (var item in properties)
{
/* field */
var field = new CodeMemberField(typeof(string), $"_{item}");
/* property */
var property = new CodeMemberProperty()
{
Name = item,
Attributes = MemberAttributes.Public | MemberAttributes.Final,
Type = new CodeTypeReference(typeof(string)),
GetStatements =
{
new CodeMethodReturnStatement(
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), $"_{item}" ))
},
SetStatements =
{
new CodeAssignStatement(
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), $"_{item}"),
new CodePropertySetValueReferenceExpression())
}
};
property.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(ImporterHeaderAttribute)),
new CodeAttributeArgument("Name", new CodePrimitiveExpression(item)),
new CodeAttributeArgument("Description", new CodePrimitiveExpression(item))));
gClass.Members.Add(field);
gClass.Members.Add(property);
}
代码编译成 dll
var sb = new StringBuilder();
var tx = new StringWriter(sb);
CodeDomProvider.CreateProvider("CSharp").GenerateCodeFromCompileUnit(compileUnit, tx, new CodeGeneratorOptions()
{
BracingStyle = "C",
IndentString = " "
});
var syntaxTree = SyntaxFactory.ParseSyntaxTree(sb.ToString());
var references = GetReferences(); // GetReferences(); 获取需要的引用
var compilation = CSharpCompilation.Create(creationDllName) // creationDllName 生成的dll文件名
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) //OutputKind.DynamicallyLinkedLibrary 生成的文件类型
.AddSyntaxTrees(syntaxTree)
.AddReferences(references);
var emitResult = compilation.Emit(outPath); // outPath dll 保存路径
// 最终通过 emitResult.Success 来判断编译是否成功
// 获取引用集
private static IEnumerable<MetadataReference> GetReferences()
{
var references = new List<MetadataReference>();
var locations = new List<string>();
#region system reference
var refLocation = typeof(object).GetTypeInfo().Assembly.Location;
var basePath = Directory.GetParent(refLocation);
locations.Add(refLocation);
refLocation = Path.Combine(basePath.FullName, "System.dll");
locations.Add(refLocation);
refLocation = typeof(JsonSerializer).Assembly.Location;
locations.Add(refLocation);
// ........
#endregion
references.AddRange(locations.Select(x => MetadataReference.CreateFromFile(x)).ToList());
return references;
}
贴一下反编译之后的源码内容
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Magicodes.ExporterAndImporter.Core;
public class PurchaseImportInfo
{
private string _Name;
private string _Age;
private string _Address;
private string[] _ParamFields = new string[3]
{
"Name",
"Age",
"Address"
};
[ImporterHeader(Name = "Name", Description = "Name")]
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
[ImporterHeader(Name = "Age", Description = "Age")]
public string Age
{
get
{
return _Age;
}
set
{
_Age = value;
}
}
[ImporterHeader(Name = "Address", Description = "Address")]
public string Address
{
get
{
return _Address;
}
set
{
_Address = value;
}
}
[IEIgnore]
public string CustomizeParam => InitCustomParam(_ParamFields);
private string InitCustomParam(global::System.Collections.Generic.IEnumerable<string> fields)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Expected O, but got Unknown
StringBuilder val = new StringBuilder();
val.Append("{");
int num = 0;
global::System.Collections.Generic.IEnumerator<string> enumerator = fields.GetEnumerator();
try
{
while (((global::System.Collections.IEnumerator)enumerator).MoveNext())
{
string current = enumerator.get_Current();
object value = base.GetType().GetProperty(current).GetValue((object)this);
if (Enumerable.Count<string>(fields) - 1 == num)
{
val.Append($"\"{current}\":\"{value}\"");
}
else
{
val.Append($"\"{current}\":\"{value}\",");
}
num++;
}
}
finally
{
((global::System.IDisposable)enumerator)?.Dispose();
}
val.Append("}");
return ((object)val).ToString();
}
}