[英]“An object reference is required for the non-static field, method or property.” while compiling C# code at runtime
I have a public class, "CodeCompiler", which allows me to compile and run C# code at run-time. 我有一个公共类“ CodeCompiler”,它使我可以在运行时编译和运行C#代码。 Just like IDEs work.
就像IDE一样。
When I click the "button1" it creates code at run-time, compiles and executes. 当我单击“ button1”时,它将在运行时创建代码,进行编译和执行。
My main Form1 contains a TextBox control called "textbox1". 我的主Form1包含一个名为“ textbox1”的TextBox控件。 In order to make changes on that "textbox1" by runtime I made this button1_Click event.
为了在运行时对“ textbox1”进行更改,我创建了这个button1_Click事件。 But when I click it, it shows me a run-time error...
但是当我单击它时,它显示了一个运行时错误...
Compiler Errors :
Line 14,34 : An object reference is required for the non-static field, method, or property 'Compiling_CSharp_Code_at_Runtime.Form1.textBox1'
It showed me when I was editing the text data on that "textbox1". 它显示了我何时编辑“ textbox1”上的文本数据。 But if I will try to make changes about other property like "Size", "Location" then imagine what will happen!
但是,如果我尝试对其他属性(例如“大小”,“位置”)进行更改,那么请想象会发生什么!
using System;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
namespace Compiling_CSharp_Code_at_Runtime
{
public class CodeCompiler
{
public CodeCompiler()
{
}
public object ExecuteCode(string code, string namespacename, string classname,
string functionname, bool isstatic,
string[] References1, params object[] args)
{
object returnval = null;
CompilerParameters compilerparams = new CompilerParameters();
for (int i = 0; i <= References1.GetUpperBound(0); i++)
{
compilerparams.ReferencedAssemblies.Add(References1[i]);
}
Assembly asm = BuildAssembly(code, compilerparams);
object instance = null;
Type type = null;
if (isstatic)
{
type = asm.GetType(namespacename + "." + classname);
}
else
{
instance = asm.CreateInstance(namespacename + "." + classname);
type = instance.GetType();
}
MethodInfo method = type.GetMethod(functionname);
returnval = method.Invoke(instance, args);
return returnval;
}
private Assembly BuildAssembly(string code, CompilerParameters compilerparams)
{
Microsoft.CSharp.CSharpCodeProvider provider = new CSharpCodeProvider();
ICodeCompiler compiler = provider.CreateCompiler();
compilerparams.GenerateExecutable = false;
compilerparams.GenerateInMemory = true;
CompilerResults results = compiler.CompileAssemblyFromSource(compilerparams, code);
if (results.Errors.HasErrors)
{
StringBuilder 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;
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
CodeCompiler cc = new CodeCompiler();
string SourceCode1 = @"
using Compiling_CSharp_Code_at_Runtime;
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
namespace N1
{
public class C1
{
public static void F1(string st1, string st2)
{
Compiling_CSharp_Code_at_Runtime.Form1.textBox1.Text += ""This is a DEMO "" st1 + st2.ToUpper();
}
}
}";
string namespace1 = "N1", class1 = "C1", function1 = "F1";
bool IsStatic = true;
object o = cc.ExecuteCode(SourceCode1, namespace1, class1, function1, IsStatic, new string[] { "Compiling CSharp Code at Runtime.exe", "System.Windows.Forms.dll", "System.Drawing.dll", "System.ComponentModel.dll", "System.dll" }, "arg1", "arg2");
}
}
}
I found many problems related to this problem on this site where a suggestion was: 我在这个建议为的站点上发现了许多与此问题相关的问题:
"It looks like I am calling a non static property from a static method. I should either make the property static, or create an instance of Form1." “看来我正在从静态方法中调用非静态属性。我应该使该属性成为静态属性,或者创建Form1的实例。”
But even creation an instance of Form1 was difficult at runtime! 但是,即使在运行时创建Form1实例也很困难!
The problem appears to be that you're not passing reference to the Form1
on which you want code to be executed to CodeCompiler
at all. 问题似乎是您根本没有将要在其上执行代码的
Form1
引用传递给CodeCompiler
。 The fact that you're calling it within your Form1
doesn't change anything - objects don't automatically learn anything about the object using them. 您在
Form1
中调用它的事实不会更改任何内容-对象不会自动使用它们学习有关该对象的任何信息。 (If they did, things would be a lot more complicated.) (如果这样做的话,事情将会复杂得多。)
The way you're accessing Form1
is also incorrect - you're using the typename (by the way, as a fully-qualified path which is pointless, because while the class C1
is not in the same namespace as Form1
, you include Form1
's namespace in the compiled code via using
) to refer to a non-static member of the type. 您访问
Form1
方式也不正确-您正在使用类型名(顺便说一句,它是毫无意义的完全限定路径,因为类C1
与Form1
不在同一个命名空间中,但您要包含Form1
'通过using
)来引用已编译代码中的s命名空间,以引用该类型的非静态成员。 The instance member textBox1
of the type Form1
can be accessed only from some instance, but there's no logical way to access the Form1 object. 该实例成员
textBox1
类型Form1
只能从一些实例来访问,但有访问Form1 的对象不符合逻辑的方式。 What if you'd instantiated 10 of them? 如果您实例化了10个该怎么办? How would the decision be made which of those 10 to return to your call?
如何决定在这10个电话中,哪一个返回您的通话?
What you should do, if you want to continue down this path (and I'm not really sure why you're trying to mimic eval() in C# - you're throwing away a lot of the safety that makes C# nice and easy to work with), is pass a reference to the Form1
instance you want altered either in the constructor of CodeCompiler or in the ExecuteCode method. 如果您想继续沿着这条道路走,应该做些什么(我不确定为什么要在C#中模仿eval()),您将放弃很多使C#变得简单易用的安全性(可以使用)传递给您想要在CodeCompiler的构造函数或ExecuteCode方法中更改的
Form1
实例的引用。 I think the latter would probably make more sense. 我认为后者可能更有意义。
You should change your SourceCode1 so that it looks like this (this also fixes a typo in the original code, restoring a missing +
character): 您应该更改SourceCode1使其看起来像这样(这也可以修复原始代码中的错字,恢复丢失的
+
字符):
string SourceCode1 = @"
using Compiling_CSharp_Code_at_Runtime;
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
namespace N1
{
public class C1
{
public static void F1(string st1, string st2, Form1 formToExecuteOn)
{
formToExecuteOn.textBox1.Text +=
""This is a DEMO "" + st1 + st2.ToUpper();
}
}
}";
Then call the ExecuteCode()
method like this: 然后像这样调用
ExecuteCode()
方法:
object o = cc.ExecuteCode(SourceCode1, namespace1, class1, function1, IsStatic,
new string[] { "Compiling CSharp Code at Runtime.exe",
"System.Windows.Forms.dll", "System.Drawing.dll",
"System.ComponentModel.dll", "System.dll" },
"arg1", "arg2", this);
This will compile the code such that the Form1
instance to be used can be passed to the method when it's invoked. 这将编译代码,以便要使用的
Form1
实例在调用时可以传递给该方法。 And the reference to that Form1
instance is provided in the argument list that is actually passed for the method invocation (ie the last argument, this
).. 在实际上为方法调用传递的参数列表(即最后一个参数
this
)中提供了对该Form1
实例的引用。
Granted, even if that works, it'll only allow you to execute code on Form1
objects. 当然,即使可行,它也只允许您在
Form1
对象上执行代码。 The greater point of this is that if you're going to go down this path, you need to be passing reference to anything you want altered to CodeCompiler
somehow. 更大的一点是,如果您要走这条路,则需要将对您想要以某种方式更改的任何内容的引用传递给
CodeCompiler
。 I'm not sure whether object objectToChange
would work instead of formToChange
, and how much information the compiler is going to need about the object you're passing in order to execute code on it. 我不知道对象是否
objectToChange
反而会工作的formToChange
,多少信息,编译器将需要约你传递,以在其上执行代码的对象。
And once more, for feeling: you need a very, very unique use case to make any of this even remotely a good use of time or sanity. 再说一次,感觉:您需要一个非常非常独特的用例,以使其中的任何一个都可以很好地利用时间或理智。 (You have to pass seven precisely-constructed objects, mostly strings, to
ExecuteCode()
every single time you want to run anything!) (您每次想运行任何东西时 ,都必须将七个精确构造的对象(主要是字符串)传递给
ExecuteCode()
!)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.