В процессе разработки программного обеспечения иногда возникает потребность выполнить какую-то часть кода программы без полного построения (build-а) проекта. Это может потребоваться при использовании стороннего компонента и/или фреймворка, когда необходимо убедиться, работает ли код, который взаимодействует с этим компонентом, как нужно либо имеются какие-то проблемы (некорректное/неправильное использование, непонимание и т.д.). Также могут быть какие-то сложные вложенные циклы, ветвления, манипуляции с данными (разбор XML, HTML) которые хотелось бы проверить.
Для таких случаев решение будет в написании отдельного приложения или тестов, независимых от основной части проекта, служащих для проверки требуемого функционала. Это удовлетворит потребности, однако со временем этот код потеряет актуальность и наверняка будет удален.
В статье C# консоль для выполнения простых «скриптов» автор приводит консольное приложение, которое позволяет компилировать и выполнять введенный код.
Для решения задач, описанных выше, мне захотелось расширить данное приложение: в первую очередь сделать приложение оконным – чтобы можно было быстро копировать/вставлять и редактировать введенный текст и результаты. Также нужно добавить функционал, который позволит писать код, использующий сторонние сборки.
Исходный код класса, позволяющего выполнять части кода, a также некоторые поясняющие комментарии приведены ниже:
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
using
Microsoft.CSharp;
using
System.CodeDom.Compiler;
using
System.Reflection;
namespace CharpShell
{
//Определения делегата для организации вывода
результатов выполнения частей кода
public delegate void ExecuteLogHandler(object
message);
public class CharpExecuter
{
//Код
готовый к выполнению
string
formatedProgramText;
public string LastProgramText
{
get { return formatedProgramText; }
}
//Поле
делегата объявлено статическим для того, чтобы можно было
//вызывать
из программы, которая будет компилироваться
public static ExecuteLogHandler OnExecute;
//Список
сборок, которые будут подключатся при компиляции
private List<string>
refferences = new List<string>();
public List<string>
Refferences
{
get
{ return refferences; }
set
{ refferences = value; }
}
//Список
using определений,
которые будут добавлены начало кода
private List<string>
usings = new List<string>();
public List<string>
Usings
{
get
{ return usings; }
set
{ usings = value; }
}
//Предопределенные
части программы. Добавляется публичный статический
//метод
ScriptMethod,
который будет вызываться из основного приложения
//внутри
используется Stopwatch для вычисления
времени выполнения программы
//Также
объявлен метод Log, который вызывает OnExecute во внешней сборке (см. ниже)
readonly string header = @"
namespace CScript
{
public class Script
{
public static void ScriptMethod()
{
Stopwatch sw = new
Stopwatch();
sw.Start();
";
readonly
string footer = @"
sw.Stop();Log(sw.Elapsed.ToString());
}
static void Log(object
message)
{
if(CharpShell.CharpExecuter.OnExecute != null)
CharpShell.CharpExecuter.OnExecute(message);
}
}
}";
public
CharpExecuter(ExecuteLogHandler onExecute)
{
OnExecute += onExecute;
//Инициализация
сборок, которые будут добавлены по умолчанию
refferences.AddRange(new string[]
{
"System.dll",
"System.Core.dll",
"System.Net.dll",
"System.Data.dll",
"System.Drawing.dll",
"System.Windows.Forms.dll",
//Необходимо
добавить свою сборку, чтобы можно было вызывать OnExecute
Assembly.GetAssembly(typeof(CharpExecuter)).Location,
});
//Инициализация
using которые будут
добавлены по умолчанию
usings.AddRange(new string[]
{
"System",
"System.IO",
"System.Net",
"System.Threading",
"System.Collections.Generic",
"System.Text",
"System.Text.RegularExpressions",
"System.ComponentModel",
"System.Data",
"System.Drawing",
"System.Diagnostics",
"System.Linq",
"System.Windows.Forms"
});
}
//Выполнение
кода
public void Execute(List<string> code)
{
//Форматирование
сырого кода (добавление предопределенный частей)
FormatSources(code);
//Выполнение
Execute();
}
public void Execute()
{
Execute(formatedProgramText);
}
public void Execute(string
program)
{
//Создание
класса CSHarpProvider с
указанием того, что сборка генерируется в памяти
var CSHarpProvider
= CSharpCodeProvider.CreateProvider("CSharp");
CompilerParameters
compilerParams = new CompilerParameters()
{
GenerateExecutable = false,
GenerateInMemory = true,
};
//Добавление
сборок для компиляции
compilerParams.ReferencedAssemblies.AddRange(refferences.ToArray());
//Компиляция
var
compilerResult = CSHarpProvider.CompileAssemblyFromSource(compilerParams,
program);
if
(compilerResult.Errors.Count == 0)
{
OnExecute(string.Concat("Executing...",
Environment.NewLine));
try
{
//Вызов метода ScriptMethod в сборке которая скомпилировалась
compilerResult.CompiledAssembly.GetType("CScript.Script").GetMethod("ScriptMethod").Invoke(null, null);
OnExecute(string.Empty);
OnExecute("Done.");
}
catch
(Exception e)
{
OnExecute(e.InnerException.Message + "rn"
+ e.InnerException.StackTrace);
}
}
else
{
foreach
(var oline in
compilerResult.Output)
OnExecute(oline);
}
}
//Форматирование
кода (добавление предопределенных частей)
public string FormatSources(string
text)
{
string
usings = FormatUsings();
formatedProgramText = string.Concat(usings, header, text, footer);
return
formatedProgramText;
}
public string FormatSources(List<string> code)
{
StringBuilder
sb = new StringBuilder(header);
foreach
(var sc in
code)
{
sb.AppendLine(sc);
}
sb.AppendLine(footer);
formatedProgramText = sb.ToString();
return
formatedProgramText;
}
//Форматирование
определений using
private string FormatUsings()
{
StringBuilder
sb = new StringBuilder();
foreach
(string using_str in
usings)
sb.AppendFormat("using {0};{1}", using_str, Environment.NewLine);
return
sb.ToString();
}
}
}
Для компиляции кода в памяти используется класс CSharpCodeProvider. Для организации перенаправления вывода результатов используется свойство делегатного типа OnExecute, которое инициализируется в конструкторе.
Ниже приведен код использования класса в клиентском приложении:
//...
CharpExecuter cs;
//Инициализация
private
void Form1_Load(object
sender, EventArgs e)
{
cs = new
CharpExecuter(new
ExecuteLogHandler(Log));
}
//Перенаправление
вывода в textbox
public void Log(object msg)
{
textBox2.Text += string.Concat(msg, Environment.NewLine);
}
//Выполнение
введенного кода
private
void button1_Click(object
sender, EventArgs e)
{
textBox2.Text = string.Empty;
cs.FormatSources(textBox1.Text);
cs.Execute();
}
//...
Приложение обладает следующими возможностями:
- Позволяет быстро вставить/запустить код на языке С# и получить результаты, которые можно также быстро скопировать в буфер обмена для последующего использования
- Сохранять получившуюся программу
- Загружать код из файла
- Изменять список using выражений и подключаемых сборок
- Определять и выводить время выполнения кода
Таким образом получилась небольшая удобная утилита для повседневной работы.
Вы можете скачать и изменить код на свое усмотрение. Полные исходные тексты находятся в репозитории на BitBucket здесь. Архив с исполняемыми файлами можно найти в разделе downloads.
This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see
ОтветитьУдалитьhttp://go.microsoft.com/fwlink/?LinkID=533240
Harrah's casino - DrMCD
ОтветитьУдалитьHotel is Harrah's hotel casino 청주 출장안마 on lake of 전라북도 출장샵 fun, a short 여수 출장안마 drive from Harrahs Lake Tahoe and 문경 출장샵 the 공주 출장샵 beautiful Lake Tahoe Tahoe.