I want to know if there is any good way of programmatically producing C# code without ac开发者_如何学JAVAtually manipulating strings or StringBuilders. Additionally it should check if the code compiles, but I guess this can be done using CSharpCodeProvider.
I'm looking for something like the following:
CodeUnit unit = new CodeUnit();
unit.AddDefaultUsings();
unit.AddUsing("MyApi.CoolNameSpace", "MyApi.Yay");
var clazz = unit.AddClass("GeneratedClass", Access.Public);
clazz.AddConstructor("....");
if(unit.Compile() != true)
//oh dang, somethings wrong!
else unit.WriteUTF8To("GeneratedClass.cs");
This might be part of the core library (Don't think CSharpCodeProvider can do this?) or an external library, but this is not my forte at all (dynamically producing code using c#), so if this seems clueless it's because I am!
That's exactly what CodeDOM is for:
var unit = new CodeCompileUnit();
var @namespace = new CodeNamespace("GeneratedCode");
unit.Namespaces.Add(@namespace);
// AddDefault() doesn't exist, but you can create it as an extension method
@namespace.Imports.AddDefault();
@namespace.Imports.Add(new CodeNamespaceImport("MyApi.CoolNameSpace"));
var @class = new CodeTypeDeclaration("GeneratedClass");
@namespace.Types.Add(@class);
@class.TypeAttributes = TypeAttributes.Class | TypeAttributes.Public;
var constructor = new CodeConstructor();
constructor.Attributes = MemberAttributes.Public;
constructor.Parameters.Add(
new CodeParameterDeclarationExpression(typeof(string), "name"));
constructor.Statements.Add(…);
@class.Members.Add(constructor);
var provider = new CSharpCodeProvider();
var result = provider.CompileAssemblyFromDom(new CompilerParameters(), unit);
Although it can be quite verbose. Also, it tries to be language-independent, which means you can't use C#-specific features like static classes, extension methods, LINQ query expressions or lambdas using this API. What you can do though, is to put any string inside method body. Using this, you can use some of the C#-specific features, but only using string manipulation, which you were trying to avoid.
You can use Reflection.Emit classes like MethodBuilder
. Also expression trees with them. It is rather CLS than C#.
I've done a wrapper around codedom. You only need to create your own C# script and specify the types being used. Namespaces and assemblies will automatically be included.
Example
public interface IWorld
{
string Hello(string value);
}
string code = @"namespace MyNamespace
{
class Temp : IWorld
{
public string Hello(string value)
{
return ""World "" + value;
}
}
}";
Compiler compiler = new Compiler();
compiler.AddType(typeof(string));
compiler.Compile(code);
var obj = compiler.CreateInstance<IWorld>();
string result = obj.Hello("World!");
Note that it was a long time ago that I wrote it. The example might not work 100%. (The Compiler class do work, the example might use it incorrectly).
Source code:
http://fadd.codeplex.com/SourceControl/changeset/view/67972#925984
I like to use the NVelocity Template Engine.
精彩评论