我从代码中动态编译代码:
string code = @"
namespace CodeInjection
{
public static class DynConcatenateString
{
public static string Concatenate(string s1, string s2){
return s1 + "" ! "" + s2;
}
}
}";
// http://stackoverflow.com/questions/604501/generating-dll-assembly-dynamically-at-run-time
Console.WriteLine("Now doing some injection...");
Console.WriteLine("Creating injected code in memory");
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
//parameters.OutputAssembly = "DynamicCode.dll"; // if specified creates the DLL
CompilerResults results = icc.CompileAssemblyFromSource(parameters, code);
然后我可以用反射调用方法:
Console.WriteLine("Input two strings, and I will concate them with reflection:");
var s1 = Console.ReadLine();
var s2 = Console.ReadLine();
var result = (string)results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString").GetMethod("Concatenate").Invoke(null, new object[] { s1, s2 });
Console.WriteLine();
Console.WriteLine("Result:");
Console.WriteLine(result);
但我想调用这样的东西:
Console.WriteLine("Input two strings, and I will concate them with dynamic type:");
var s1 = Console.ReadLine();
var s2 = Console.ReadLine();
dynamic type = results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString");
var resultA = (string)type.Concatenate(s1, s2); // runtime error
// OR
var resultB = (string)CodeInjection.DynConcatenateString.Concatenate(s1, s2); // compile error (cannot find assembly)
Console.WriteLine();
Console.WriteLine("Result:");
Console.WriteLine(resultA);
Console.WriteLine(resultB);
结果B会更好。有什么想法怎么做?我需要严格的.NET 4.0,我们还没有更新到4.5(因为团队的一半使用VS 2010)。 (我可以用反射来调用,我知道,我只是在寻找另一种方式,因为我们需要测试dyn。代码)
答案 0 :(得分:4)
在此方案中,您无法直接使用dynamic
。 dynamic
总是需要一个类实例,但是你试图在静态类中调用一个方法,因此你没有一个类实例。
但是,您可以创建一个帮助程序类,并将其与dynamic
结合使用:
public class StaticMethodInvoker : DynamicObject
{
Type _containingType;
public StaticMethodInvoker(Type containingType)
{
_containingType = containingType;
}
public override bool TryInvokeMember(
InvokeMemberBinder binder, Object[] args, out Object result)
{
result = _containingType.InvokeMember
binder.Name,
BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public,
null, null, args);
return true;
}
}
用法:
var type = results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString");
dynamic DynConcatenateString = new StaticMethodInvoker(type);
string result = DynConcatenateString.Concatenate(s1, s2);
答案 1 :(得分:2)
您正在调用static
方法。使用dynamic
,您可以绕过支票,但实际上您正试图在Concatenate()
上为System.Type
拨打CodeInjection.DynConcatenateString
。
首先使它成为一个实例方法:
public class DynConcatenateString
{
public string Concatenate(string s1, string s2){
return s1 + "" ! "" + s2;
}
}
现在让我们看看你的代码:
dynamic type = results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString");
这是System.Type
,而不是CodeInjection.DynConcatenateString
类型的对象。如果您将dynamic
更改为var
,则会在编译时看到正确的类型。然后,您必须创建该类型的实例,如下所示:
var type = results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString");
dynamic instance = Activator.CreateInstance(type);
对于语法B,C#没有希望,因为CodeInjection.DynConcatenateString
在编译时不存在,那么该行将失败。
如果您必须保持静态,那么您所能做的就是使用反射来调用该方法(dynamic
然后无用)。不要担心性能损失...... DLR并不比普通的反射快得多(作为AFAIK,它是如何通过缓存来实现的。)
答案 2 :(得分:0)
为什么不使用泛型类型? 定义您的类接口:
public interface IDynConcatenateString
{
string Concatenate(string s1, string s2);
}
然后生成动态代码
public T GetInstanceOf<T>(string code, string typename)
{
Console.WriteLine("Now doing some injection...");
Console.WriteLine("Creating injected code in memory");
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
//parameters.OutputAssembly = "DynamicCode.dll"; // if specified creates the DLL
CompilerResults results = icc.CompileAssemblyFromSource(parameters, code);
//type name = "CodeInjection.DynConcatenateString"
T codeclass = (T)results.CompiledAssembly.CreateInstance(typename);
return codeclass;
}
尝试像这样执行......
public void Exec()
{
string code = @"
namespace CodeInjection
{
public class MyDynConcatenateString : IDynConcatenateString
{
public string Concatenate(string s1, string s2){
return s1 + "" ! "" + s2;
}
}
}";
IDynConcatenateString writer = GetInstanceOf<IDynConcatenateString>(
code, "CodeInjection.MyDynConcatenateString");
var s1 = Console.ReadLine();
var s2 = Console.ReadLine();
var result = writer.Concatenate(s1, s2);
Console.WriteLine(result);
}
IDynConcatenateString实现是动态的,在“code”变量中定义,作为Exec方法的参数。 您只需要定义接口,而无需对方法名称或类名进行硬编码。
答案 3 :(得分:0)