Вопрос или проблема
Предположим, у меня есть нечто подобное:
string singleStatement = "System.DateTime.Now";
Есть ли способ взять singleStatement
и разобрать и выполнить его во время выполнения?
Так что:
DateTime currentTime = singleStatement.SomeCoolMethodToRunTheText();
присвоило бы значение DateTime.Now
переменной currentTime
.
Посмотрите это (цитата ниже).
Это возможно: посмотрите на System.CodeDom
и System.CodeDom.Compiler
.
Я нашел пример, который написал несколько месяцев назад:
предположим, usingList
– это arraylist
со всеми операторными указателями (без ключевого слова using, System.Xml
например)
предположим, importList
– это arraylist
со всеми именами dll, которые необходимы для компиляции (system.dll
например)
предположим, source
– это исходный код, который вы хотите скомпилировать
предположим, classname
– это имя класса, который вы хотите скомпилировать
предположим, methodname
– это имя метода
Посмотрите на следующий код:
//Создание метода
CodeMemberMethod pMethod = new CodeMemberMethod();
pMethod.Name = methodname;
pMethod.Attributes = MemberAttributes.Public;
pMethod.Parameters.Add(new
CodeParameterDeclarationExpression(typeof(string[]),"boxes"));
pMethod.ReturnType=new CodeTypeReference(typeof(bool));
pMethod.Statements.Add(new CodeSnippetExpression(@"
bool result = true;
try
{
" + source + @"
}
catch
{
result = false;
}
return result;
"));
//Создание класса
CodeTypeDeclaration pClass =
new System.CodeDom.CodeTypeDeclaration(classname);
pClass.Attributes = MemberAttributes.Public;
pClass.Members.Add(pMethod);
//Создание пространства имен
CodeNamespace pNamespace = new CodeNamespace("myNameSpace");
pNamespace.Types.Add(pClass);
foreach(string sUsing in usingList)
pNamespace.Imports.Add(new
CodeNamespaceImport(sUsing));
//Создание единицы компиляции
CodeCompileUnit pUnit = new CodeCompileUnit();
pUnit.Namespaces.Add(pNamespace);
//Установка параметров компиляции
CompilerParameters pParams =
new CompilerParameters((string[])importList.ToArray(typeof(string)));
pParams.GenerateInMemory = true;
//Компиляция
CompilerResults pResults =
(new CSharpCodeProvider())
.CreateCompiler().CompileAssemblyFromDom(pParams, pUnit);
if (pResults.Errors != null && pResults.Errors.Count>0)
{
foreach(CompilerError pError in pResults.Errors)
MessageBox.Show(pError.ToString());
result =
pResults.CompiledAssembly.CreateInstance("myNameSp ace."+classname);
}
Например,
если 'usingList' равно
{
"System.Text.RegularExpressions"
}
если 'importList' равно
{
"System.dll"
}
если 'classname' равно "myClass"
если 'methodName' равно "myMethod"
если 'source' равно "
string pays=@"ES
FR
EN
"
Regex regex=new Regex(@"^[A-Za-z]
{
2
}
$");
result=regex.IsMatch(boxes[0]);
if (result)
{
regex=new Regex(@"^"+boxes[0]+@".$",RegexOptions.Multiline);
result=regex.Matches(pays).Count!=0;
}
Тогда код, который будет скомпилирован, будет следующим:
using System.Text.RegularExpressions;
namespace myNameSpace
{
public class myClass
{
public bool myMethod(string[] boxes)
{
bool result=true;
try
{
string pays=@"ES
FR
EN
"
Regex regex=new Regex(@"^[A-Za-z]
{
2
}
$");
result=regex.IsMatch(boxes[0]);
if (result)
{
regex=new Regex(@"^"+boxes[0]+@".$",RegexOptions.Multiline);
result=regex.Matches(pays).Count!=0;
}
}
catch
{
result=false;
}
return result;
}
}
}
Это возможно только с использованием сервисов компилятора и Reflection.Emit()
, которые будут компилировать и загружать сборку в память.
Посмотрите здесь.
Вы можете компилировать код во время выполнения, используя объект CSharpCodeProvider. Хотите ли вы действительно это сделать или нет, вопрос спорный. 🙂
Ответ или решение
Вопрос о том, как выполнить код, который находится в строке, является интересным и многогранным в контексте программирования на языке C#. Чтобы успешно решить данную задачу, необходимо использовать возможности, предоставляемые библиотекой .NET, такие как CodeDom и CSharpCodeProvider. Рассмотрим этот процесс более подробно.
Основные понятия
Когда нам нужно выполнить строку кода в C#, мы сталкиваемся с задачей динамической компиляции и выполнения. Самый распространенный способ это сделать — использовать класс CSharpCodeProvider
из пространства имен System.CodeDom.Compiler
. Этот класс позволяет генерировать и компилировать C# код на лету.
Шаги выполнения кода из строки
-
Сбор информации: Определите необходимые директивы
using
, имена классов и методов, а также код, который вам необходимо скомпилировать и выполнить. -
Создание структуры кода: Используйте классы
CodeNamespace
,CodeTypeDeclaration
, иCodeMemberMethod
для создания кода, который будет скомпилирован. -
Компиляция кода: Используйте метод
CompileAssemblyFromDom
для компиляции созданного кода. -
Выполнение кода: После успешной компиляции вы можете создать экземпляр класса и вызвать нужные методы.
Вот пример того, как это можно сделать:
using System;
using System.CodeDom.Compiler;
using System.CodeDom;
using Microsoft.CSharp;
public class DynamicCodeExecutor
{
public static object ExecuteCode(string code, string className, string methodName)
{
// Создаем пространство имен и класс
CodeNamespace codeNamespace = new CodeNamespace("DynamicNamespace");
CodeTypeDeclaration codeClass = new CodeTypeDeclaration(className);
// Создаем метод
CodeMemberMethod codeMethod = new CodeMemberMethod();
codeMethod.Name = methodName;
codeMethod.ReturnType = new CodeTypeReference(typeof(object));
// Добавляем код, который будет выполняться в методе
codeMethod.Statements.Add(new CodeSnippetExpression(code));
codeClass.Members.Add(codeMethod);
codeNamespace.Types.Add(codeClass);
// Создаем компиляционную единицу
CodeCompileUnit codeUnit = new CodeCompileUnit();
codeUnit.Namespaces.Add(codeNamespace);
// Параметры компиляции
CompilerParameters parameters = new CompilerParameters
{
GenerateInMemory = true
};
parameters.ReferencedAssemblies.Add("mscorlib.dll");
parameters.ReferencedAssemblies.Add("System.dll");
// Компилируем код
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults results = provider.CompileAssemblyFromDom(parameters, codeUnit);
// Проверяем на наличие ошибок компиляции
if (results.Errors.Count > 0)
{
foreach (CompilerError error in results.Errors)
{
Console.WriteLine($"Error ({error.ErrorNumber}): {error.ErrorText}");
}
return null;
}
// Создаем экземпляр и вызываем метод
object obj = results.CompiledAssembly.CreateInstance($"DynamicNamespace.{className}");
return obj.GetType().GetMethod(methodName)?.Invoke(obj, null);
}
}
class Program
{
static void Main()
{
string code = "return DateTime.Now;"; // Код для выполнения
object result = DynamicCodeExecutor.ExecuteCode(code, "DynamicClass", "DynamicMethod");
Console.WriteLine(result);
}
}
Объяснение кода
- CodeNamespace и CodeTypeDeclaration — обеспечивают создание структуры пространств имен и классов.
- CodeMemberMethod — определяет метод, который будет содержать выполненный код.
- CodeSnippetExpression — используется для добавления произвольного кода.
- CompileAssemblyFromDom — компилирует сгенерированный код и возвращает объект
CompilerResults
, содержащий информацию о результате компиляции. - CreateInstance и Invoke — используются для создания экземпляра скомпилированного класса и вызова метода.
Безопасность
Перед выполнением скомпилированного кода необходимо учитывать аспекты безопасности. Особенно, если код поступает от ненадежного источника, необходимо тщательно фильтровать входные данные и проверять их на наличие потенциальных угроз.
Заключение
Выполнение кода из строки в C# — задача, требующая внимательного подхода и правильного использования средств компиляции. Подход, описанный выше, предоставляет мощные инструменты для динамического выполнения программного кода, и при этом позволяет гибко управлять созданием и контролем выполнения кода.