Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Как программно откомпилировать класс и вызвать его метод?


Есть программа для обработки данных, и несколько раз в день меняется формула расчетов. Т.е. надо каждый раз вносить изменения в код определенного метода и компилировать сборку.
Вносить изменения в код метода можно программно, просто меняя фрагмент текста.
А как после замены формулы из своей программы откомпилировать код, вызвать мето
и получить результат?  
    


Ответы

Ответ 1



[c:\temp\code.cs] using System; using System.Linq; class Test { static public int Run(params int[] values) { return values.Sum(); // эта строка часто меняется } } Этот код компилирует code.cs, вызывает метод Test.Run и выводит результат. using System; using Microsoft.CSharp; using System.CodeDom.Compiler; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { var code = System.IO.File.ReadAllText(@"c:\test\code.cs"); var cps = new CompilerParameters() { GenerateInMemory = true, GenerateExecutable = false }; cps.ReferencedAssemblies.AddRange(new[] { "System.dll", "System.Core.dll" }); var cp = new CSharpCodeProvider(); var cr = cp.CompileAssemblyFromSource(cps, code); foreach (var err in cr.Errors) Console.WriteLine(err); if (!cr.Errors.HasErrors) { var m = cr.CompiledAssembly.GetType("Test").GetMethod("Run"); var res = m.Invoke(null, new object[] { new[] { 1, 2, 3 } }); Console.WriteLine(res); } } } } [Output] 6 Если значение var code указано прямо в Main, например: var code = @" using System; // ... "; то в code надо убрать \r\n var cr = cp.CompileAssemblyFromSource(cps, Regex.Replace(code, "[\r\n]", " ")); иначе при компиляции будет ошибка: A namespace cannot directly contain members such as fields or methods.

Ответ 2



Добавлю, что при использовании сборок не из GAC нужно помещать скомпилированую сборку в каталог с используемыми DLL static void ДобавитьссылкинаDLLВКаталоге(Type тип, System.Collections.Specialized.StringCollection ReferencedAssemblies) { string ИмяФайлаСборки = тип.Assembly.Location; string Каталог = Path.GetDirectoryName(ИмяФайлаСборки); string ИмяФайла = Path.GetFileName(ИмяФайлаСборки); string[] files = Directory.GetFiles(Каталог, "*.dll"); // Console.WriteLine("Всего файлов {0}.", files.Length); foreach (string f in files) { if (! String.Equals(f, ИмяФайлаСборки, System.StringComparison.CurrentCultureIgnoreCase)) if (ReferencedAssemblies.IndexOf(f) == -1) ReferencedAssemblies.Add(f); } } static CompilerResults СкомпилироватьОбертку(string строкаКласса,string ИмяКласса) { bool ЭтоСборкаГак = typeof(T).Assembly.GlobalAssemblyCache; string Путь = Path.GetDirectoryName(typeof(T).Assembly.Location); string OutputAssembly = Path.Combine(Путь, ИмяКласса) + ".dll"; var compiler = new CSharpCodeProvider(); var parameters = new CompilerParameters(); parameters.ReferencedAssemblies.Add("System.dll"); parameters.ReferencedAssemblies.Add("System.Core.dll"); parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll"); parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll"); parameters.ReferencedAssemblies.Add(typeof(AutoWrap).Assembly.Location); if (!ЭтоСборкаГак) { parameters.ReferencedAssemblies.Add(typeof(T).Assembly.Location); ДобавитьссылкинаDLLВКаталоге(typeof(T), parameters.ReferencedAssemblies); } else { string ИмяСборки = typeof(T).Assembly.ManifestModule.Name; if (parameters.ReferencedAssemblies.IndexOf(ИмяСборки) == -1) parameters.ReferencedAssemblies.Add(ИмяСборки); } if (ЭтоСборкаГак) parameters.GenerateInMemory = true; else { // parameters.GenerateInMemory = true; parameters.GenerateInMemory = false; parameters.OutputAssembly = OutputAssembly; } parameters.GenerateExecutable = false; parameters.IncludeDebugInformation = true; var res = compiler.CompileAssemblyFromSource(parameters, строкаКласса); return res; } Кроме того можно использовать Scripting-API String ПолучитьСтрокуДелегата() { string returnStr = @"return (MatchEvaluator)((match) => { string x = match.Value; // If the first char is lower case... if (char.IsLower(x[0])) { // Capitalize it. return char.ToUpper(x[0]) + x.Substring(1, x.Length - 1); } return x; });"; return returnStr; } private void button_Click(object sender, RoutedEventArgs e) { // textBoxEval.AppendText(((D)СоздатьДелегат())().ToString()); string words = "надо заменить все первые буквы в словах на заглавные"; string pattern = @"\w+"; // MatchEvaluator evaluator = (MatchEvaluator)ПолучитьДелегат(); var scr = Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default .WithReferences(typeof(MatchEvaluator).Assembly) .WithImports("System", "System.Text.RegularExpressions"); var result = Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync(ПолучитьСтрокуДелегата(), scr).Result; MatchEvaluator evaluator = (MatchEvaluator)result; textBoxEval.AppendText(Regex.Replace(words, pattern, evaluator)); } Или более сложный вариант string ПолучитьСтрокуКласса() { var res = @" class ClassTest { string строка; public Func ЗаданнаяСтрока; public ClassTest(string Строка) { строка = Строка; ЗаданнаяСтрока = () => строка; } public static Func ПолучитьДелегатОбъекта(string Строка) { var obj = new ClassTest(Строка); return obj.ЗаданнаяСтрока; } } return new Func>(ClassTest.ПолучитьДелегатОбъекта); "; return res; } private void button2_Click(object sender, RoutedEventArgs e) { var scr = Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default .WithImports("System"); var result = Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync(ПолучитьСтрокуКласса(), scr).Result; var делегат = (Func>)result; textBoxEval.AppendText(делегат("Тестовая строка 2")() + Environment.NewLine); } Кроме того можно использовать DynamicMethod Для рефлектора есть дизассемблер в Reflection.Emit

Комментариев нет:

Отправить комментарий