简体   繁体   中英

Is it possible to execute C# code represented as string?

On my form I have a button click

private void button1_Click(object sender, EventArgs e)
{
    do something                           
}

How on the click would I load my do something from a text file, for example my text file looks like this:

MessageBox.Show("hello");
label1.Text = "Hello";

on click it does everything in my text file, if possible.

Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.

var provider = CodeDomProvider.CreateProvider("C#");
string src=@"
    namespace x
    {
        using System;
        public class y
        {
            public void z()
            {
                Console.WriteLine(""hello world"");
            }
        }
    }
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
    var type = result.CompiledAssembly.GetType("x.y");
    var instance = Activator.CreateInstance(type);
    type.GetMethod("z").Invoke(instance, null);
}

Edit

As @Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1 , a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:

string src = @"
namespace x
{
    using System.Windows;
    public class y
    {
        public void z(Label label1)
        {
            MessageBox.Show(""hello"");
            label1.Text = ""Hello"";
        }
    }
}
";

Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:

var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
    var type = result.CompiledAssembly.GetType("x.y");
    var instance = Activator.CreateInstance(type);
    var method = type.GetMethod("z");
    var args = new List<object>();

    // assume any parameters are properties/fields of the current object
    foreach (var p in method.GetParameters())
    {
        var prop = this.GetType().GetProperty(p.Name);
        var field = this.GetType().GetField(p.Name);
        if (prop != null)
            args.Add(prop.GetValue(this, null));
        else if (field != null);
            args.Add(field.GetValue(this));
        else
            throw new InvalidOperationException("Parameter " + p.Name + " is not found");
    }
    method.Invoke(instance, args.ToArray());
}

Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.

But no one @BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx

It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.

By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.

You can find what isn't implemented here.

Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.

The key is passing in a HostObject . It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.

Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.

public class MyHostObject
{
    public string Value1 { get; set; }
    public string Value2 { get; set; }
}

public class RoslynTest
{
    public void Test()
    {
        var myHostObject = new MyHostObject
        {
            Value1 = "Testing Value 1",
            Value2 = "This is Value 2"
        };

        var engine = new ScriptEngine();
        var session = engine.CreateSession(myHostObject);
        session.AddReference(myHostObject.GetType().Assembly.Location);
        session.AddReference("System");
        session.AddReference("System.Core");
        session.ImportNamespace("System");

        // "Execute" our method so we can call it.
        session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");

        var s = session.Execute<string>("UpdateHostObject()");

        //s will return "V1V2" and your instance of myHostObject was also changed.
    }

}

No. You can not. At least in any simple way. The thing you want is something like eval('do something') from javascript. That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).

The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.

UPDATED:

Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he

Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM