说我有一种我不断重复的模式.就像是:
static class C {
[DllImport("mydll")]
private static extern uint MyNativeCall1(Action a);
public static uint MyWrapper1(Action a) {
// Do something
return MyNativeCall1(a);
}
[DllImport("mydll")]
private static extern uint MyNativeCall2(Action a);
public static uint MyWrapper2(Action a) {
// Do something
return MyNativeCall2(a);
}
//...
[DllImport("mydll")]
private static extern uint MyNativeCallN(Action a);
public static uint MyWrapperN(Action a) {
// Do something
return MyNativeCallN(a);
}
}
Run Code Online (Sandbox Code Playgroud)
所有这些中唯一不同的是本机函数和包装器方法的名称.有没有办法通过类似装饰器的东西生成它们?起初我认为C#属性是装饰器.也就是说,我可以通过类似的方式生成代码[GenerateScaffolding("MyNativeCall1")].但似乎属性更像是注释,实例化一个包含一些元数据的类.
C#也没有宏.有没有办法做到这一点?
要记住以下几点:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
static class C {
<#
int N = 15;
for(int i=0; i<N; i++)
{ #>
[DllImport("mydll")]
private static extern uint MyNativeCall<#= i #>(Action a);
public static uint MyWrapper<%#= i #>(Action a) {
return MyNativeCall<#= i #>(a);
}
<# } #>
}
Run Code Online (Sandbox Code Playgroud)
您不需要 IDE 在运行时生成和处理模板,但您必须创建自己的指令处理器和/或主机。
Engine engine = new Engine();
//read the text template
string input = File.ReadAllText(templateFileName);
//transform the text template
string output = engine.ProcessTemplate(input, host);
Run Code Online (Sandbox Code Playgroud)
在您的模板中,您可以将模板语言与 C# 代码混合使用(示例 HTML 生成):
<table>
<# for (int i = 1; i <= 10; i++)
{ #>
<tr><td>Test name <#= i #> </td>
<td>Test value <#= i * i #> </td>
</tr>
<# } #>
</table>
Run Code Online (Sandbox Code Playgroud)
下面是我如何使用 T4从文本文件生成各种状态机。
您甚至可以在运行时为C# 类生成源代码,从您的程序编译、加载和执行。
如果您将所有这些技术结合起来,甚至可能与诸如MEF 之类的可组合部件结合使用,我相信您将能够实现您所需要的。
没有 MEF 的UPDATE ,但您仍然需要 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);
}
<# } #>
}
Run Code Online (Sandbox Code Playgroud)
和这个程序:
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");
//object instance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("MyWrapper4");
Console.WriteLine(method.Invoke(null, new object[]{squareRoot}));
}
}
}
Run Code Online (Sandbox Code Playgroud)
它将打印 2,因为它是 4 的平方根。
更新 2
从上面的第二个链接稍微修改 CustomCmdLineHost 后:
public IList<string> StandardAssemblyReferences
{
get
{
return new string[]
{
//If this host searches standard paths and the GAC,
//we can specify the assembly name like this.
//---------------------------------------------------------
//"System"
//Because this host only resolves assemblies from the
//fully qualified path and name of the assembly,
//this is a quick way to get the code to give us the
//fully qualified path and name of the System assembly.
//---------------------------------------------------------
typeof(System.Uri).Assembly.Location,
typeof(System.Linq.Enumerable).Assembly.Location
};
}
}
Run Code Online (Sandbox Code Playgroud)
示例程序不再需要 IDE:
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
Run Code Online (Sandbox Code Playgroud)
我希望这能满足您的需求。
更新 3
如果您像这样进一步修改示例主机:
internal string TemplateFileValue = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,"CustomCmdLineHost.tt");
Run Code Online (Sandbox Code Playgroud)
然后你就可以避免指定模板文件名而只使用内存处理:
var host = new CustomCmdLineHost();
Engine engine = new Engine();
string input = File.ReadAllText("ExtDll.tt");
string source = engine.ProcessTemplate(input, host);
Run Code Online (Sandbox Code Playgroud)
享受并亲切地标记您的首选答案。
我想我会悲伤地回答自己:不。在 C# 中没有办法做到这一点。也就是说,语言本身和框架中都没有任何内容。
然而,如果使用 Visual Studio,就会有模板,就像 RGraham 和 Pradeep 指出的那样;其他 IDE 可能也有不同的设施/功能来完成此任务。但同样,C# 本身没有什么比预处理器或装饰器更好的了。
| 归档时间: |
|
| 查看次数: |
2086 次 |
| 最近记录: |