我有一个命令处理程序,基本上这样工作:
ControlList.Handlers[CommandType.MyCommandComesHere].Handle(data);
Handlers
是Dictionary<CommandType, ICommandHandler>
,CommandType
是枚举。
Handle
轮流将导致它:
using System;
using log4net;
namespace My_Application
{
public class MyCommand : ICommandHandler
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(MyCommand));
public void Handle(Events data)
{
Console.WriteLine("I can load cs files on the fly yay!!");
}
}
}
我的问题是如何才能使我的应用程序编译并让我在运行时使用该cs文件?
任何简单的例子都会非常受欢迎,但不是必需的,只要我能得到一些关于我需要寻找什么的指示,因为我甚至不确定我需要做什么才能实现。
为了简单起见,我目前正在尝试了解如何将cs文件加载到已经编译并且当前正在运行的应用程序中。
答案 0 :(得分:5)
使用CodeDOM,您需要先创建a compiler provider。 (出于您的目的,您可能需要将GenerateExecutable
设置为false
并将GenerateInMemory
设置为true
。)
var csc = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
然后,您可以使用CompileAssemblyFromSource
编译程序集并从中返回CompilerResults
。从此返回的对象中,使用其CompiledAssembly
属性获取对生成的程序集的引用。
var results = csc.CompileAssemblyFromSource(parameters, "contents of the .cs file");
var assembly = results.CompiledAssembly;
然后你可以使用该程序集中的create instances反射并调用它们的方法。
var instance = assembly.CreateInstance("MyCommand");
// etc...
或者,如果您只对短代码段感兴趣,那么use Roslyn可能值得。您需要先创建ScriptEngine
。
var engine = new ScriptEngine();
如果您确信字符串中的表达式返回可分配给Execute
的类型,那么您可以Execute<T>
上的字符串 - 或T
。
var myObject = engine.Execute("1+1");
var myInt = engine.Execute<int>("1+1");
它肯定更直接,因此值得研究是否符合您的目的。
答案 1 :(得分:1)
我已经寻找不同的方法来实现这一点,并发现cs脚本库轻量级且可用。以下是我如何使用它的代码片段。它在应用程序域中运行cs代码,因此它假定正在编译的cs脚本来自可信源。
using CSScriptLibrary;
using csscript;
using System.CodeDom.Compiler;
using System.Reflection;
//Method example - variable script contains cs code
//This is used to compile cs to DLL and save DLL to a defined location
public Assembly GetAssembly(string script, string assemblyFileName)
{
Assembly assembly;
CSScript.CacheEnabled = true;
try
{
bool debugBuild = false;
#if DEBUG
debugBuild = true;
#endif
if (assemblyFileName == null)
assembly = CSScript.LoadCode(script, null);
else
assembly = CSScript.LoadCode(script, assemblyFileName, debugBuild, null);
return assembly;
}
catch (CompilerException e)
{
//Handle compiler exceptions
}
}
/// <summary>
/// Runs the code either form script text or precompiled DLL
/// </summary>
public void Run(string script)
{
try
{
string tmpPath = GetPathToDLLs(); //Path, where you store precompiled DLLs
string assemblyFileName;
Assembly assembly = null;
if (Directory.Exists(tmpPath))
{
assemblyFileName = Path.Combine(tmpPath, GetExamScriptFileName(exam));
if (File.Exists(assemblyFileName))
{
try
{
assembly = Assembly.LoadFrom(assemblyFileName); //Načtení bez kompilace
}
catch (Exception exAssemblyLoad)
{
Tools.LogError(exAssemblyLoad.Message);
assembly = null;
}
}
}
else
assemblyFileName = null;
//If assembly not found, compile it form script string
if (assembly ==null)
assembly = GetAssembly(script, assemblyFileName);
AsmHelper asmHelper = new AsmHelper(assembly);
//This is how I use the compiled assembly - it depends on your actual code
ICalculateScript calcScript = (ICalculateScript)asmHelper.CreateObject(GetExamScriptClassName(exam));
cex = calcScript.Calculate(this, exam);
Debug.Print("***** Calculated {0} ****", exam.ZV.ZkouskaVzorkuID);
}
catch (Exception e)
{
//handle exceptions
}
}