Проблема
В вашем приложении нужно выполнять код, который не известен на этапе компиляции приложения. Это могут быть разнообразные плагины, расширения, вычисления и т.д.
Решение
.NET позволяет компилировать и выполнять код. Более того, исполнять код можно безопасно, сузив круг ресурсов, которые может использовать выполняемый код.
Реализация
Для компиляции нам потребуется CodeDomProvider, с помощью которого можно инстанцировать экземпляр компилятора для любого языка .NET. Перейдем к коду.
- private string EvalCode(string typeName, string methodName, string sourceCode)
- {
- string output = ":)";
- var compiler = CodeDomProvider.CreateProvider("CSharp");
- var parameters = new CompilerParameters
- {
- CompilerOptions = "/t:library",
- GenerateInMemory = true,
- IncludeDebugInformation = true
- };
- var results = compiler.CompileAssemblyFromSource(parameters, sourceCode);
- if (!results.Errors.HasErrors)
- {
- var assembly = results.CompiledAssembly;
- var evaluatorType = assembly.GetType(typeName);
- var evaluator = Activator.CreateInstance(evaluatorType);
- output = (string) InvokeMethod(evaluatorType, methodName, evaluator, new object[] { output });
- return output;
- }
- output = "\r\nHouston, we have a problem at compile time!";
- return results.Errors.Cast<CompilerError>().Aggregate(output, (current, ce) => current + string.Format("\r\nline {0}: {1}", ce.Line, ce.ErrorText));
- }
- [FileIOPermission(SecurityAction.Deny, Unrestricted = true)]
- private object InvokeMethod(Type evaluatorType, string methodName, object evaluator, object[] methodParams)
- {
- return evaluatorType.InvokeMember(methodName, System.Reflection.BindingFlags.InvokeMethod, null, evaluator, methodParams);
- }
Как видите, ничего сверхъестественного нет. Создаем компилятор, передаем в него код, получаем DLL. С помощью reflection запускаем нужный метод.
Код в действии:
Интересна строка 27. С помощью нее можно ограничивать права, которыми обладает код (не только динамический). В .NET есть механизм Code Access Security, благодаря которому можно управлять безопасностью выполняемого кода.
Ошибка безопасности при попытки записи на диск:
Несмотря на то что выполнение кода выглядит весьма «безобидно», стоит помнить, что это весьма ресурсоемкий процесс. Например, если выполнять код в текущем домене приложения (так, как это сделано в демо), при продолжительной работе приложения память может просто закончится (.NET не может выгружать сборки из домена).
Демо:
DynDllFunWf.zip