你不需要IDE在运行时生成和处理模板,但是你必须
创建自己的指令处理器和/或
托管。
Engine engine = new Engine();
//read the text template
string input = File.ReadAllText(templateFileName);
//transform the text template
string output = engine.ProcessTemplate(input, host);
在您的模板中,您可以将模板语言与C#代码混合使用(生成示例HTML):
<table>
<# for (int i = 1; i <= 10; i++)
{ #>
<tr><td>Test name <#= i #> </td>
<td>Test value <#= i * i #> </td>
</tr>
<# } #>
</table>
这是我如何使用T4从文本文件
生成各种状态机的方式。
你甚至可以为
C#类在运行时生成源代码,编译和加载并从程序中执行。
如果您将所有这些技术结合起来,甚至包括可组合部件,例如
MEF,我相信您将能够实现所需的目标。
更新 没有MEF,但仍然需要IDE预处理模板。
由于我没有您的DLL,因此无法给出确切答案,但也许这会有所帮助。
给定此模板(ExtDll.tt):
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="mscorlib" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#
var extraCodeArray = new[]
{ string.Empty,
"var localVar = 1;",
"var localVar = 2;",
"var localVar = 3;",
"var localVar = 4;",
"var localVar = 5;",
"var localVar = 6;",
"var localVar = 7;",
"var localVar = 8;",
"var localVar = 9;",
"var localVar = 10;",
};
#>
using System;
static class C{
<# for (int i = 1; i <= 10; i++)
{ #>
public static double MyWrapper<#= i #>(Func<int,double> a) {
<#= extraCodeArray[i] #>
return a.Invoke(localVar);
}
<# } #>
}
和这个程序:
using System;
using System.Linq;
namespace ConsoleApplication4
{
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
class Program
{
static void Main(string[] args)
{
ExtDll code = new ExtDll();
string source = code.TransformText();
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters()
{
GenerateInMemory = true,
GenerateExecutable = false
};
parameters.ReferencedAssemblies.AddRange(
new[]
{
"System.Core.dll",
"mscorlib.dll"
});
CompilerResults results = provider.CompileAssemblyFromSource(parameters, source);
if (results.Errors.HasErrors)
{
var errorString = String.Join("\n", results.Errors.Cast<CompilerError>().Select(error => String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText)));
throw new InvalidOperationException(errorString);
}
Assembly assembly = results.CompiledAssembly;
Func<int,double> squareRoot = (i) => { return Math.Sqrt(i); };
Type type = assembly.GetType("C");
MethodInfo method = type.GetMethod("MyWrapper4");
Console.WriteLine(method.Invoke(null, new object[]{squareRoot}));
}
}
}
它会打印出2,因为它是4的平方根。
更新2
在略微修改第二个链接中的CustomCmdLineHost后:
public IList<string> StandardAssemblyReferences
{
get
{
return new string[]
{
typeof(System.Uri).Assembly.Location,
typeof(System.Linq.Enumerable).Assembly.Location
};
}
}
示例程序不再需要集成开发环境:
var host = new CustomCmdLineHost();
host.TemplateFileValue = "ExtDll.tt";
Engine engine = new Engine();
string input = File.ReadAllText("ExtDll.tt");
string source = engine.ProcessTemplate(input, host);
if (host.Errors.HasErrors)
{
var errorString = String.Join("\n", host.Errors.Cast<CompilerError>().Select(error => String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText)));
throw new InvalidOperationException(errorString);
}
CSharpCodeProvider provider = new CSharpCodeProvider();
... rest of the code as before
我希望这能满足您的需求。
更新3
如果您进一步修改示例主机如下:
internal string TemplateFileValue = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,"CustomCmdLineHost.tt")
然后您可以避免指定模板文件名,直接使用内存处理:
var host = new CustomCmdLineHost();
Engine engine = new Engine();
string input = File.ReadAllText("ExtDll.tt");
string source = engine.ProcessTemplate(input, host);
享受并友好地标记您喜欢的答案。
DllImport
的EntryPoint
属性来指定被调用方法的不同名称呢? - Shlomi Borovitz[DllImport("mydll", EntryPoint = "MyNativeCall1")] public static extern uint MyWrapper1(Action a);
- Shlomi Borovitz