使用codedom自动生成代码

刚刚接触自动代码生成,便小试牛刀,解决了项目中的一些问题。

问题:我们的项目分成很多层次,当增加一个方法的时候,会显得异常繁琐,但每个层次之间的调用大同小异,所以尝试使用代码生成。现在假设有Engine, EngineBase,LocalEngine, RemoteEngine,Host等几个类,其定义和关系如下:

 1     public class EngineFacade
 2     {
 3         private EngineBase engine = null;
 4         public EngineFacade(bool isLocalEngine = true)
 5         {
 6             if (isLocalEngine)
 7             {
 8                 engine = new LocalEngine();
 9             }
10             else
11             {
12                 engine = new RemoteEngine();
13             }
14         }
15 
16         public bool GetCurrentStatus()
17         {
18             return true;
19         }
20     }
21 
22     public abstract class EngineBase
23     {
24         public abstract bool GetCurrentStatus();
25     }
26 
27     public class LocalEngine : EngineBase
28     {
29         public override bool GetCurrentStatus()
30         {
31             return true;
32         }
33     }
34 
35     public class RemoteEngine : EngineBase
36     {
37         private IHost host = null;
38 
39         public RemoteEngine()
40         {
41             host = new Host();
42         }
43 
44         public override bool GetCurrentStatus()
45         {
46             return host.GetCurerntStatus();
47         }
48     }
49 
50     public interface IHost
51     {
52         bool GetCurerntStatus();
53     }
54 
55     public class Host : IHost
56     {
57         public bool GetCurerntStatus()
58         {
59             return true;
60         }
61     }
层次关系定义

上诉定义的类会被SampleClient调用:

    public class SampleClient
    {
        public void Test()
        {
            var engine = new EngineFacade(false);
            Console.WriteLine(engine.GetCurrentStatus());
        }
    }
SampleClient

如果我们需要增加一个新的方法SetStatus,如何避免额外的体力劳动呢?

解决方案:使用CodeDom进行动态代码生成。

  1 class CodeGenerator
  2     {
  3         private CodeCompileUnit targetUnit = new CodeCompileUnit();
  4         private CodeTypeDeclaration targetClass = new CodeTypeDeclaration("Test");
  5         private static readonly string targetInstance = "engineInstance";
  6 
  7         public CodeGenerator()
  8         {
  9             CodeNamespace sample = new CodeNamespace("CodeDomTest");
 10             sample.Imports.Add(new CodeNamespaceImport("System"));
 11             targetClass.IsClass = true;
 12             targetClass.TypeAttributes = TypeAttributes.Public;
 13             sample.Types.Add(targetClass);
 14             targetUnit.Namespaces.Add(sample);
 15         }
 16 
 17         public void AddMethod(string name, Type returnType, string comments, CodeExpression codeExpression)
 18         {
 19             CodeMemberMethod method = new CodeMemberMethod();
 20             method.Attributes = MemberAttributes.Public | MemberAttributes.Override;
 21             method.Name = name;
 22             method.ReturnType = new CodeTypeReference(returnType);
 23             method.Comments.Add(new CodeCommentStatement(comments));
 24             CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement();
 25             returnStatement.Expression = codeExpression;
 26             method.Statements.Add(returnStatement);
 27             targetClass.Members.Add(method);
 28         }
 29 
 30         public void AddMethod(string name, Type returnType, string comments, CodeExpression codeExpression, string paraName, Type paraType)
 31         {
 32             CodeMemberMethod method = new CodeMemberMethod();
 33             method.Attributes = MemberAttributes.Public | MemberAttributes.Override;
 34             method.Name = name;
 35             method.ReturnType = new CodeTypeReference(returnType);
 36             method.Comments.Add(new CodeCommentStatement(comments));
 37             method.Parameters.Add(new CodeParameterDeclarationExpression(paraType, paraName));
 38             CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement();
 39             returnStatement.Expression = codeExpression;
 40             method.Statements.Add(returnStatement);
 41             targetClass.Members.Add(method);
 42         }
 43 
 44         public void AddMethod(string name, Type returnType, string comments, CodeExpression codeExpression, List<CodeParameterDeclarationExpression> parameters)
 45         {
 46             CodeMemberMethod method = new CodeMemberMethod();
 47             method.Attributes = MemberAttributes.Public | MemberAttributes.Override;
 48             method.Name = name;
 49             method.ReturnType = new CodeTypeReference(returnType);
 50             method.Comments.Add(new CodeCommentStatement(comments));
 51             method.Parameters.AddRange(parameters.ToArray());
 52             CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement();
 53             returnStatement.Expression = codeExpression;
 54             method.Statements.Add(returnStatement);
 55             targetClass.Members.Add(method);
 56         }
 57 
 58 
 59         public void AddAbstractMethod(string name, Type returnType, string comments)
 60         {
 61             CodeMemberMethod method = new CodeMemberMethod();
 62             method.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
 63             method.Name = name;
 64             method.ReturnType = new CodeTypeReference(returnType);
 65             method.Comments.Add(new CodeCommentStatement(comments));
 66             targetClass.Members.Add(method);
 67         }
 68 
 69         public void AddAbstractMethod(string name, Type returnType, string comments, string paraName, Type paraType)
 70         {
 71             CodeMemberMethod method = new CodeMemberMethod();
 72             method.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
 73             method.Name = name;
 74             method.ReturnType = new CodeTypeReference(returnType);
 75             method.Parameters.Add(new CodeParameterDeclarationExpression(paraType, paraName));
 76             method.Comments.Add(new CodeCommentStatement(comments));
 77             targetClass.Members.Add(method);
 78         }
 79 
 80         public void AddField(string name, Type fieldType)
 81         {
 82             CodeMemberField member = new CodeMemberField();
 83             member.Attributes = MemberAttributes.Private;
 84             member.Name = name;
 85             member.Type = new CodeTypeReference(fieldType);
 86             targetClass.Members.Add(member);
 87         }
 88 
 89         public void GenerateCode()
 90         {
 91             CodeDomProvider provider = new CSharpCodeProvider();
 92             CodeGeneratorOptions options = new CodeGeneratorOptions();
 93             options.BracingStyle = "C";
 94             using (StreamWriter streamWriter = new StreamWriter("SampleReactorCode.cs"))
 95             {
 96                 provider.GenerateCodeFromCompileUnit(targetUnit, streamWriter, options);
 97             }
 98         }
 99 
100         public static void Start()
101         {
102             CodeGenerator sample = new CodeGenerator();
103             Test(sample);
104             sample.GenerateCode();
105         }
106 
107         private static void Test(CodeGenerator sample)
108         {
109             var methodToAdd = "Test";
110             var returnType = typeof(Dictionary<string, object>);
111             var paraname = "key";
112             var paraType = typeof(string);
113             var parTypes = new Dictionary<string, Type>() { { "no", typeof(int) }, { paraname, paraType } };
114 
115             var parameters = GenerateParameters(parTypes);
116             var parArguments = GenerateParametersArguments(parTypes).ToArray();
117 
118             sample.AddMethod(methodToAdd, returnType, "This is for engine facade",
119                 new CodeMethodInvokeExpression(
120                     new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), targetInstance),
121                     methodToAdd, parArguments), parameters);
122             sample.AddAbstractMethod(methodToAdd, returnType, "This is for engine base", paraname, paraType);
123             sample.AddMethod(methodToAdd, returnType, "This is for local engine",
124                 new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "scenario"),
125                     methodToAdd, parArguments), parameters);
126             sample.AddMethod(methodToAdd, returnType, "This is for remote engine",
127                 new CodeMethodInvokeExpression(
128                     new CodeTypeReferenceExpression(new CodeTypeReference("HostProxy")), methodToAdd,
129                     parArguments), parameters);
130 
131             var dump = parArguments.ToList();
132             parameters.Insert(0, new CodeParameterDeclarationExpression(typeof(string), "engineKey"));
133             dump.Insert(0, new CodeArgumentReferenceExpression("engineKey"));
134             parArguments = dump.ToArray();
135 
136             sample.AddMethod(methodToAdd, returnType, "This is for host",
137                 new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("InstanceManager"), methodToAdd, parArguments),
138                 parameters);
139 
140             dump.Insert(0, new CodePrimitiveExpression(methodToAdd));
141             dump.Insert(0, new CodeArgumentReferenceExpression("host_name"));
142             parArguments = dump.ToArray();
143             sample.AddMethod(methodToAdd, returnType, "This is for host",
144                 new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("host"), string.Format("Invoke<{0}>", returnType.ToString())
145                     , parArguments), parameters);
146         }
147 
148         private static List<CodeParameterDeclarationExpression> GenerateParameters(Dictionary<string, Type> parameterTypes)
149         {
150             return parameterTypes.Select(parameterType => new CodeParameterDeclarationExpression(parameterType.Value, parameterType.Key)).ToList();
151         }
152         private static List<CodeExpression> GenerateParametersArguments(Dictionary<string, Type> parameterTypes)
153         {
154             return parameterTypes.Select(parameterType => (CodeExpression)new CodeArgumentReferenceExpression(parameterType.Key)).ToList();
155         }
156     }
CodeGenerator

如何使用CodeDom,请参考动态源代码生成和编译

原文地址:https://www.cnblogs.com/allanli/p/3593948.html