Implementing a C# Read-Evaluate-Print-Loop in Code
Here's some handy code to embed a Read-Eval-Print-Loop (REPL) in a C# dll. I used this to write command scripts to automate processes in a web application.
The interpreter method is embedded in an object from which we want to call instance methods from the command line even though the method is in another dll. This will let us run another method on the object from the command line.
/// Command interpreter for an object. This allows methods to be called from the command line interactively. /// All methods take a TextWriter as the first object to allow feedback. /// Used with the Wizard front end. /// </summary> /// <param name="textWriter">For web apps this is "Response.Out", for Console use "Console.Out"</param> /// <param name="args">arguments to method call</param> public void Command(TextWriter textWriter, string[] args) { object[] param = new object[args.Length]; param[0] = textWriter; //args[0] is the name of the method, so we can overwrite for(int i=1;i<args.Length;i++) { //load all the args into rest of param array param[i] = args[i]; } MethodInfo methodInfo = this.GetType().GetMethod(args[0]);//try to get method if(methodInfo != null) { //invoke if successful methodInfo.Invoke(this,param); } else { textWriter.WriteLine(String.Format("unknown command \"\"","args[0]")); } }
The Wizard front end creates an object from another dll, maybe inside a web application dll and calls the "Command" method.
using System; namespace Wizard { class Wizard { ////// Invokes methods on objects in a dll (use config instead of hard coded path) /// [STAThread] static void Main(string[] args) { object o = Activator.CreateInstanceFrom(@"C:\workfiles\MyProject\src\MyProject\bin\MyProject.dll", "MyProject.Admin.Operations").Unwrap(); o.GetType().GetMethod("Command").Invoke(o,new object[]{Console.Out,args}); } } }
Methods to be invoke look something like this. Note this method can be called in a web context or from the command line.
using System; using System.IO; using System.Net; using System.Reflection; namespace MyProject.Admin { public class Operations : System.Web.UI.Page { ... public void LoadGroup(TextWriter textWriter, string ingroup) { ArrayList groups = new ArrayList(); ... foreach(string group in groups) { textWriter.Write("
Loading group, \""+group+"\""); textWriter.Flush(); Group.LoadFromDataDirectory(group,textWriter); textWriter.Flush(); } } ...
The following command will create an Operations object and invoke the "LoadGroup" method and pass "MyGroup" as an argument:
c:\workfiles\Wizard\bin\Debug> Wizard.exe LoadGroup MyGroup