C# 在运行时编译和访问类

C# 在运行时编译和访问类,c#,winforms,console,runtime,C#,Winforms,Console,Runtime,我正在尝试向我的应用程序添加一些特定的模块功能,我需要用户能够创建自己的类,然后在运行时将它们导入到程序中,这些类将遵循特定的模板,以便我可以在新类中调用函数 例如,一个类可以是: 我会编译这个类,然后执行doSomething 如何在C中实现这一点?如果您的用户拥有必要的工具,如Visual Studio,如果他们正在编写C类,他们就应该拥有这些工具,他们可以为您提供DLL,然后您可以动态加载: private static T CreateInstance(string assemblyNa

我正在尝试向我的应用程序添加一些特定的模块功能,我需要用户能够创建自己的类,然后在运行时将它们导入到程序中,这些类将遵循特定的模板,以便我可以在新类中调用函数

例如,一个类可以是:

我会编译这个类,然后执行doSomething


如何在C中实现这一点?

如果您的用户拥有必要的工具,如Visual Studio,如果他们正在编写C类,他们就应该拥有这些工具,他们可以为您提供DLL,然后您可以动态加载:

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);
}
存在T generic参数,因此您可以传递抽象类或接口以将类型实例强制转换为:

public interface IDoSomething
{
    bool DoSomething();
}
用户在编写自己的类时从该接口继承:

public class UserDefinedClass : IDoSomething
{
    public bool DoSomething()
    {
        // Implementation here.
    }
}
这允许您保留类型安全性并直接调用类方法,而不是依赖反射来实现

如果您确实希望用户提供C源代码,您可以这样做:

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;
    }
}
然后,您只需在上面的CreateInstance代码中替换生成的程序集,而不是外部加载的程序集。请注意,您的用户仍然需要在其类的顶部提供适当的using语句


互联网上也有一些地方用C语言解释,以防你真的不需要一个完整的类。

如果你的用户有必要的工具,如Visual Studio,他们在编写C类时确实应该有这些工具,他们可以为你提供一个DLL,然后你可以动态加载:

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);
}
存在T generic参数,因此您可以传递抽象类或接口以将类型实例强制转换为:

public interface IDoSomething
{
    bool DoSomething();
}
用户在编写自己的类时从该接口继承:

public class UserDefinedClass : IDoSomething
{
    public bool DoSomething()
    {
        // Implementation here.
    }
}
这允许您保留类型安全性并直接调用类方法,而不是依赖反射来实现

如果您确实希望用户提供C源代码,您可以这样做:

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;
    }
}
然后,您只需在上面的CreateInstance代码中替换生成的程序集,而不是外部加载的程序集。请注意,您的用户仍然需要在其类的顶部提供适当的using语句


Internet上也有一些地方用C语言解释,以防您真的不需要一个完整的类。

通常,您可以通过向用户提供一个dll来实现这一点,该dll为您的应用程序提供挂钩。这使他们能够创建一个dll,然后您的应用程序可以加载该dll。我使用了一些程序,让您只需将一个.cs文件放到一个目录中,并对其进行编译和运行。请参见Darin Dimitrov的回答:您可以查看LINQPad的工作原理:。不完全一样,因为用户在LINQPad的UI中键入代码。通常,您可以通过向用户提供一个dll来实现这一点,该dll为您的应用程序提供挂钩。这使他们能够创建一个dll,然后您的应用程序可以加载该dll。我使用了一些程序,让您只需将一个.cs文件放到一个目录中,并对其进行编译和运行。请参见Darin Dimitrov的回答:您可以查看LINQPad的工作原理:。不完全一样,因为用户在LINQPad的UI中键入代码。