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.