我一直在关注这个 StackOverflow question .这是我能找到的最接近的东西,但不完全是。
在我提出问题之前,让我解释一下我的最终目标是什么,我正在制作一个支持网络的编译器平台,因此,我想在内存中做所有事情(不创建文件),所以我希望能够编译代码,然后能够引用我刚刚编译的类中的对象进行任意测试。我知道这听起来有多不安全,因此欢迎提供有关安全性的意见。
我的问题是如何将 C# 源代码编译到内存中,并创建该类的实例?
目前,我正处于可以生成有效的 .dll 并在 VisualStudio 中手动导入和使用它的阶段。
我接下来的两个步骤是:
- 自动加载程序集(这就是我要问的)
- 这意味着我不再需要提供 dll 的路径,并手动获取封闭类成员的地址
- 任意引用它的成员
- 这意味着我可以创建一个类的接口(interface),而无需预先了解类的成员,有点像 foreach 循环如何处理键值对。
为了完全在内存中尝试这个,我试过了。 (来源然后解释)
private object sourceToObj(string source) {
string source = "..."; /*my class*/
CSharpCodeProvider pro = new CSharpCodeProvider();
CompilerParameters params = new CompilerParameters();
params.GenerateInMemory = true;
params.GenerateExecutable = false;
params.ReferencedAssemblies.Add("System.dll");
CompilerResults res = pro.CompileAssemblyFromSource( params, source );
Assembly DOTasm = res.CompiledAssembly;
AppDomain domain = AppDomain.CreateDomain( "thisdomain" );
domain.load( DOTasm , /*???*/ );
domain.CreateInstanceAndUnwrap( DOTasm .FullName, /*???*/ );
return /*???*/;
}
最后,正如代码中的这一点,我希望返回一些我可以调用其属性的对象。
所以调用 object obj = new sourceToObj(source).class();
或其他东西是可能的。
沿着这条可能确实是错误的道路走下去给我留下了 3 个未知数。
- 什么是
System.Security.Policy.Evidence assemblySecurity
对象。 AppDomain.CreateInstanceAndUnwrap()
的正确参数是什么?- 然后如何将其作为对象返回?
当然,这种方法可能是错误的,它基于上面的链接,该链接很接近,但不是火鸡。
编辑:经过更多研究后,我想包含一个源文件示例。
namespace testNS {
public partial class i18Rule {
private string i18_Name;
private string i18_Value;
public void setName(string s) {
i18_name = s;
}
/* Other obvious functions */
};
};
我相信我取得了一点进展并进入了我的问题的第二个子句,如何创建它的实例。
我继续使用 AppDomain 来包含我的程序集。我还采用了写入磁盘并将其读取为字节数组的方式,正如我在这个问题中所做的那样我碰巧遇到了 Compile c# on the fly .
/* not the final method, see Philips answer for tryLoadCompiledType which validates this works */
private void sourceToUnitTest(string source, callBack CB) {
var pro = new CSharpCodeProvider();
var DOMref = AppDomain.CurrentDomain.GetAssemblies()
.Where(obj => !obj.IsDynamic)
.Select(obj => obj.Location)
.ToArray();
var Cparams = new CompilerParameters( DOMref );
Cparams.OutputAssembly = "SOURCE.DLL";
CompilerResults res = pro.CompileAssemblyFromSource(Cparams, source);
Assembly asm = res.CompiledAssembly;
Type[] allTypes = res.CompiledAssembly.GetTypes();
foreach (Type t in allTypes)
{
TryLoadCompiledType(res, t.ToString());
Debug.WriteLine(t.ToString());
}
/* I don't return I do something with each type here */
}
最佳答案
How do I compile C# source to memory, and create an instance of that class?
当我想将源代码作为输入并编译和执行它时,我遇到了类似的问题。这是我看完之后想出的Is it possible to dynamically compile and execute C# code fragments? :
public CompilerResults CompileSource(string sourceCode)
{
var csc = new CSharpCodeProvider(
new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var referencedAssemblies =
AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.FullName.StartsWith("mscorlib", StringComparison.InvariantCultureIgnoreCase))
.Where(a => !a.IsDynamic) //necessary because a dynamic assembly will throw and exception when calling a.Location
.Select(a => a.Location)
.ToArray();
var parameters = new CompilerParameters(
referencedAssemblies);
return csc.CompileAssemblyFromSource(parameters,
sourceCode);
}
然后我有一个辅助函数:
public static object TryLoadCompiledType(this CompilerResults compilerResults, string typeName, params object[] constructorArgs)
{
if (compilerResults.Errors.HasErrors)
{
Log.Warn("Can not TryLoadCompiledType because CompilerResults.HasErrors");
return null;
}
var type = compilerResults.CompiledAssembly.GetType(typeName);
if (null == type)
{
Log.Warn("Compiled Assembly does not contain a type [" + typeName + "]");
return null;
}
return Activator.CreateInstance(type, constructorArgs);
}
所以把它放在一起
public void Example(){
dynamic instance =
CompileSource("namespace Test{public class DynamicCompile{ /*method*/}}")
.TryLoadCompiledType("Test.DynamicCompile");
//can now call methods on 'instance'
}
关于C# CompilerResults GenerateInMemory?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24871955/