2

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.

  • Maybe you should look at scriptcs... – DaveShaw Jan 29 '14 at 23:47
  • So, you want to dynamically compile C# code that is inside of a text file.. ? – Simon Whitehead Jan 29 '14 at 23:47
  • With [Reflection](http://msdn.microsoft.com/en-us/library/f7ykdhsy(v=vs.110).aspx), but it will be *extremely* complex – JDB Jan 29 '14 at 23:48
  • "CompileAssemblyFromSource"? I haven't used this before, but it looks promising. http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.compileassemblyfromsource(v=vs.110).aspx – McGarnagle Jan 29 '14 at 23:53
  • DaveShaw, JBB ok thanks, was hoping it would be easy, I will look into both of these. – user3251099 Jan 29 '14 at 23:55
  • @SimonWhitehead basically yeah – user3251099 Jan 29 '14 at 23:56
  • @McGarnagle it does indeed, thanks for the suggestion – user3251099 Jan 29 '14 at 23:57
  • @JDB I really don't think reflection applies here. The OP is really calling for a *compiler*. Reflection just lets you get info about classes and methods, it can't parse source code. – McGarnagle Jan 29 '14 at 23:59
  • @McGarnagle - The Reflection assemblies [include a compiler](http://msdn.microsoft.com/en-us/library/system.reflection.emit(v=vs.110).aspx). – JDB Jan 30 '14 at 00:01
  • 2
    Or perhaps wait for [Roslyn](http://msdn.microsoft.com/en-us/vstudio/hh500769.aspx#Toc306015663)? – Branko Dimitrijevic Jan 30 '14 at 00:02
  • @BrankoDimitrijevic - From what I understand of it, Roslyn is not a compiler - it's a parser. Could be mistaken. – JDB Jan 30 '14 at 00:05
  • See this answer: http://stackoverflow.com/questions/826398/is-it-possible-to-dynamically-compile-and-execute-c-sharp-code-fragments – Simon Whitehead Jan 30 '14 at 00:05
  • What you're looking for is an *Eval function in C#*. Google that, and you'll find many ways to do it. One simple way is to use JScript instead; it does have an Eval function. – Robert Harvey Jan 30 '14 at 00:08
  • @JDB I believe it's a compiler too - after all, it is slated to replace "classic" compiler underneath Visual Studio. But I was aiming more at the Scripting APIs that are apparently also part of the project. – Branko Dimitrijevic Jan 30 '14 at 00:12
  • @JDB I stand corrected, thanks -- would be interesting to see that applied to the OP. – McGarnagle Jan 30 '14 at 00:25
  • 1
    You might have more success with a DLR approach. Here's an old example of how to dynamically execute IronRuby code from a C# app and have the IronRuby display things on the C# Form: http://jimmy.schementi.com/2009/12/ironruby-rubyconf-2009-part-35.html – Dax Fohl Jan 30 '14 at 00:42

3 Answers3

4

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());
}
McGarnagle
  • 101,349
  • 31
  • 229
  • 260
  • 1
    Well, the example is nice sample of on-the-fly compilation. However, doesn't look correct for current question. As the user asks to call some code, which uses members of the method/class from which the code is called. – Agat Jan 30 '14 at 00:11
  • Very interesting. Is it possible to unload obsolete types from the application domain? Or to re-define types by recompiling? These two functions would allow a true scripting framework. – pid Jan 30 '14 at 00:12
  • 1
    @Agat right, there's obviously work to get from my answer to the OP, but I think you can see how to approach it. – McGarnagle Jan 30 '14 at 00:13
  • @pid I don't know, I'm not even a novice in this subject -- just Googled "dynamic compilation" and ran with it ... – McGarnagle Jan 30 '14 at 00:13
  • 1
    @Agat - you are both correct-ish, I think, which goes toward my feeling that this question is "too broad". – JDB Jan 30 '14 at 00:15
  • @McGarnagle Well, that's pretty blurry. How can you see that in such way, as that 'outer' class/method (compliled on-the-fly) must communicate with our current (caller) code. – Agat Jan 30 '14 at 00:15
  • What's more sad, that people seem don't read questions or answers carefully, putting pluses and minuses. Don't you think from practical point of view this answer is not much better then another one? It does not solve the question. – Agat Jan 30 '14 at 00:18
1

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.
    }

}
TyCobb
  • 8,909
  • 1
  • 33
  • 53
  • @BrankoDimitrijevic did mention it in the comments. Would be cool if you could write a basic sample illustrating how ... – McGarnagle Jan 30 '14 at 00:50
  • 1
    @McGarnagle Whoops, missed that. I will reflect that in my answer. I'll see what I can do for code. I may end up copying something online since I no longer have access to the code I used this in. – TyCobb Jan 30 '14 at 00:53
  • Whoever went through and downvoted every answer and the question, please don't. – TyCobb Jan 30 '14 at 16:07
-1

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.

Agat
  • 4,577
  • 2
  • 34
  • 62
  • You can use reflection to compile code on-the-fly, but you are mostly correct - it's not simple. – JDB Jan 29 '14 at 23:54
  • Well, I am not sure where I said that you can not compile code on-the-fly. The only thing I mentioned, that C# is not something to work like interpreter. Also, I would not use Reflection (not sure if I got your idea correctly). It's rather a conceptual question, you can use many thing: direct call from your code (if ... else if ... else), expressions etc. – Agat Jan 29 '14 at 23:57
  • "*The only way to implement that...*" isn't *quite* accurate, but the gist of what you are saying is, in my opinion, accurate. – JDB Jan 30 '14 at 00:00
  • Hm... Well, actually, right. That's not the only way! He he I've already thought some other 'geeky' one. Updating the answer. – Agat Jan 30 '14 at 00:02
  • 2
    -1. It is entirely possible. I would suggest you look up the `Reflection.Emit` namespace and `CodeDom`. E.g, this answer: http://stackoverflow.com/questions/826398/is-it-possible-to-dynamically-compile-and-execute-c-sharp-code-fragments – Simon Whitehead Jan 30 '14 at 00:04
  • @SimonWhitehead And what's the problem with my answer? How are you going to use emitting if you don't have something to emit? – Agat Jan 30 '14 at 00:07