1

I am trying to add some specific modular functonality to my application, I need users to be able to create their own classes and then have them imported into the program at runtime, these classes will follow a specific template so I can call functions within their new class.

For example, a class could be: http://pastebin.com/90NTjia9

I would compile the class and then execute doSomething();

How would I achieve this in C#?

JamieB
  • 923
  • 9
  • 30
  • 1
    Typically you would do this by providing users with a dll that provides hooks in to your application. This enables them to create a dll which your app can then load. – mikerobi Apr 02 '12 at 16:56
  • I've used programs that let you simply drop a .cs file into a directory and have it compiled and run. – JamieB Apr 02 '12 at 16:58
  • 1
    See Darin Dimitrov's answer here: http://stackoverflow.com/questions/4718329/compiling-code-dynamically-using-c-sharp – James Johnson Apr 02 '12 at 17:02
  • You can check out how LINQPad works: http://www.linqpad.net/HowLINQPadWorks.aspx. Not quite the same thing, because users type the code in LINQPad's UI. – mbeckish Apr 02 '12 at 17:30

1 Answers1

4

If your users have the necessary tools (such as Visual Studio, which they really should have if they're writing C# classes), they can supply you with a DLL, which you can then load dynamically:

private static T CreateInstance(string assemblyName, string typeName)
{
    var assembly = Assembly.LoadFrom(assemblyName);

    if (assembly == null)
        throw new InvalidOperationException(
           "The specified assembly '" + assemblyName + "' did not load.");

    Type type = assembly.GetType(typeName);
    if (type == null)
        throw new InvalidOperationException(
           "The specified type '" + typeName + "' was not found in assembly '" + assemblyName + "'");

    return (T)Activator.CreateInstance(type);
}

The T generic parameter is there so that you can pass an abstract class or interface to cast the type instance to:

public interface IDoSomething
{
    bool DoSomething();
}

Your users inherit from this interface when they write their own class:

public class UserDefinedClass : IDoSomething
{
    public bool DoSomething()
    {
        // Implementation here.
    }
}

This allows you to retain type safety and call the class methods directly, rather than relying on Reflection to do so.

If you really want your users to provide C# source, you can compile their class at runtime like this:

private Assembly BuildAssembly(string code)
{
    var provider = new CSharpCodeProvider();
    var compiler = provider.CreateCompiler();
    var compilerparams = new CompilerParameters();
    compilerparams.GenerateExecutable = false;
    compilerparams.GenerateInMemory = true;
    var results = compiler.CompileAssemblyFromSource(compilerparams, code);
    if (results.Errors.HasErrors)
    {
        var errors = new StringBuilder("Compiler Errors :\r\n");
        foreach (CompilerError error in results.Errors )
        {
            errors.AppendFormat("Line {0},{1}\t: {2}\n", 
                   error.Line, error.Column, error.ErrorText);
        }
        throw new Exception(errors.ToString());
    }
    else
    {
        return results.CompiledAssembly;
    }
}

Then, you just substitute the resulting assembly in the CreateInstance code above, instead of the externally-loaded assembly. Note that your users will still need to provide the appropriate using statements at the top of their class.

There are also places on the Internet that explain how to get an Eval() function in C#, in case you don't really need a full-blown class.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501