「怠zyな夢」またはそれ自体のスクリプトエンジン

多くの場合、アプリケーション開発者は、システム設計時に詳細に説明されていなかったタスクの一部を解決するスクリプト言語を製品に組み込む必要があります。 それは本当に便利です。機能を拡張する可能性があり、そのようなソリューションを作成する面倒さは、一見、小さいです。



利用可能なパブリック埋め込みスクリプトツールが単純な場合、この古い夢は「怠dreamな夢」と呼ぶことができます。 既製のツールは長い間存在しており、たとえばWindowsプラットフォームでは、前世紀にIActiveScriptSite COMインターフェイスを介してVBScriptおよびJscriptインターフェイスを使用できました。 現在、たとえばLuaに基づいた他のソリューションが多数ありますが、それらにはすべて、使用したいという欲求を大きく制限する不快な機能が1つあります。



スクリプトは単独で正常に動作し、ロジックと算術演算を実行できますが、困難または不可能な場合、それらの恩恵はまったくありません。



•開発したシステムのオブジェクトにアクセスするための関数とオブジェクトを追加します。

•ソーススクリプトの構文制御を実行し、構文エラーに関するメッセージを生成します。

•実行ポイントとステータスを通知しながら、デバッガーなどのステップバイステップモードでスクリプトを実行します。



それでも、このすべてを簡単かつ直感的に行うことを望み、新しいAPIに関する多数のドキュメントを読んで眠れぬ夜を過ごす必要はありません。 悲しいかな、これは常に成功しているわけではなく、非常にまれです。



現在、アプリケーションソフトウェアは非常に頻繁にC#で記述されており、使い慣れた、しかし柔軟性があり、スクリプトを記述できるようにしたいと思います。 そのような解決策があり、細心の注意が必要です。 この名前空間 CSharpCodeProviderクラスを持つSystem.CodeDom.Compiler。 これはすべて.NET 4.0に登場しましたが、何らかの理由で、ほとんどのC#の出版物は、C#自体をベース言語として使用して、C#でスクリプトを記述する問題に対処しませんでした。 また、製品を記述し、さらにサポートするために非常に便利です。



この場合、最も重要で興味深いメソッドは、コンパイルしてエラーメッセージを生成するCompileAssemblyFromSource()であり、「Hello world!」と簡単に記述できます。



using System; using System.IO; using Microsoft.CSharp; using System.CodeDom.Compiler; using System.Reflection; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { //    StringBuilder sb = new StringBuilder(); sb.AppendLine("using System;"); sb.AppendLine("namespace ConsoleApplication1"); sb.AppendLine("{"); sb.AppendLine(" public class MyScripter"); sb.AppendLine(" {"); sb.AppendLine(" public void Hello()"); sb.AppendLine(" {"); sb.AppendLine(" Console.WriteLine(\"Hello world!\");"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine("}"); //  CSharpCodeProvider codeProvider = new CSharpCodeProvider(); CompilerResults compileResults = codeProvider.CompileAssemblyFromSource( new CompilerParameters(), new string[] { sb.ToString() }); //  ,    foreach (CompilerError err in compileResults.Errors) Console.WriteLine("Error({0:1}): {2} {3}", err.Line, err.Column, err.ErrorNumber, err.ErrorText); if (compileResults.Errors.HasErrors) return; //   dll   byte[] dllBytes = File.ReadAllBytes(compileResults.PathToAssembly); Assembly asmDll = Assembly.Load(dllBytes, null); Type objType = asmDll.GetType("ConsoleApplication1.MyScripter"); //      object oClassInst = Activator.CreateInstance(objType); //       MethodInfo entry = objType.GetMethod("Hello", new Type[] {}); entry.Invoke(oClassInst, null); } } }
      
      





実行のために実行:



画像






そのため、最も単純な形式では、C#スクリプトは正常に機能します。 スクリプトテキストを変更するだけで、その操作に影響を与えることができます。 実際には、メインプログラムのオブジェクトを例としてスクリプトに転送するだけです。



文字列型のオブジェクトは、そのようなオブジェクトとして非常に適しています。



 using System; using System.IO; using Microsoft.CSharp; using System.CodeDom.Compiler; using System.Reflection; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { string sMyStr = "Before Script."; //    StringBuilder sb = new StringBuilder(); sb.AppendLine("using System;"); sb.AppendLine("namespace ConsoleApplication1"); sb.AppendLine("{"); sb.AppendLine(" public class MyScripter"); sb.AppendLine(" {"); sb.AppendLine(" public void Hello(ref string s)"); sb.AppendLine(" {"); sb.AppendLine(" Console.WriteLine(\"Hello world!\");"); sb.AppendLine(" s=\"After Script.\";"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine("}"); //  CSharpCodeProvider codeProvider = new CSharpCodeProvider(); CompilerResults compileResults = codeProvider.CompileAssemblyFromSource( new CompilerParameters(), new string[] { sb.ToString() }); //  ,    foreach (CompilerError err in compileResults.Errors) Console.WriteLine("Error({0:1}): {2} {3}", err.Line, err.Column, err.ErrorNumber, err.ErrorText); if (compileResults.Errors.HasErrors) return; //   dll   byte[] dllBytes = File.ReadAllBytes(compileResults.PathToAssembly); Assembly asmDll = Assembly.Load(dllBytes, null); Type objType = asmDll.GetType("ConsoleApplication1.MyScripter"); //      object oClassInst = Activator.CreateInstance(objType); //       MethodInfo entry = objType.GetMethod("Hello", new Type[] { typeof(string).MakeByRefType() }); Object[] param = new Object[] { sMyStr }; Console.WriteLine(param[0]); //    entry.Invoke(oClassInst, param); //   Console.WriteLine(param[0]); //    } } }
      
      





実行のために実行:



画像






スクリプトコードから値を転送して返すことができるようになったことがわかります。 パラメータとして文字列へのリンクではなく、情報システムの内部オブジェクトを渡すと、スクリプトからシステムを適切に操作できます。



このメカニズムにはデバッグモードのランタイムがあります。これには.pdbファイルを接続する必要があるため、他にも多くの興味深い機能があります。



このアプローチの唯一の欠点は、コンパイル中にOSの一時ディレクトリにdllが作成されることです。



この欠点を解決する方法は、System.Reflection.Emitスペースを使用することにつながりますが、これは1つの記事に適したかなりボリュームのある素材です。 この場合、コンパイラと生成を個別に記述する必要があるため、これは困難です。 しかし、独自の構文を発明する機会はありますか! はい、あなた自身またはあなたの好きな猫に敬意を表して新しいプログラミング言語に名前を付けることができます。

頑張って



Arkady Pchelintsev、プロジェクトアーキテクト



All Articles