2014-11-01 23 views
-1

我有一個公共類「CodeCompiler」,它允許我在運行時編譯和運行C#代碼。就像IDE一樣工作。「非靜態字段,方法或屬性需要對象引用。」同時在運行時編譯C#代碼

當我點擊「button1」它在運行時創建代碼,編譯並執行。

我的主Form1包含一個名爲「textbox1」的TextBox控件。爲了通過運行時對「textbox1」進行更改,我製作了這個button1_Click事件。 但是,當我點擊它,它讓我看到一個運行時錯誤...

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' 

這表明我當我編輯對「TextBox1的」文本數據。但是,如果我將嘗試對「大小」,「位置」等其他屬性進行更改,那麼想象會發生什麼!

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"); 
     } 
    } 
} 

我發現與此相關的問題,在這個網站,其中一個建議是很多問題:

「它看起來像我打電話從一個靜態方法非靜態屬性 我應該要麼使財產靜態,或創建Form1的實例。「

但即使創建Form1的實例在運行時也很困難!

+0

這將工作,如果我會使該textbox1作爲一個公共和靜態!但是...... – IremadzeArchil19910311 2014-11-01 15:54:31

+0

那麼你如何期待它得到正確的實例呢?爲什麼不將形式引用傳遞給方法,或者類似的東西? – 2014-11-01 15:59:30

+0

C1類不是靜態的,這意味着你必須在使用它之前對它進行初始化。它確實有一個靜態方法,可以調用,但是任何嘗試使用沒有Initing的C1都會失敗。 – 2014-11-01 16:03:28

回答

3

問題似乎是,您沒有將要執行代碼的Form1的引用傳遞給CodeCompiler。您在Form1之內調用它的事實不會改變任何內容 - 對象不會自動獲知有關使用它們的對象的任何信息。 (如果他們這樣做,事情會變得更加複雜)。

您訪問Form1的方式也是不正確的 - 您使用的是typename(順便說一句,作爲完全合格的路徑是毫無意義的,因爲雖然類C1Form1不在同一個名稱空間中,但在編譯代碼中通過using包含Form1的名稱空間以引用該類型的非靜態成員。 Form1類型的實例成員textBox1只能從某個實例訪問,但沒有合乎邏輯的方式訪問 Form1對象。如果你實例化了其中的10個呢?如何決定哪10個返回您的電話?如果你想繼續沿着這條路走下去(而且我不確定你爲什麼試圖在C#中模仿eval() - 你扔掉了很多安全因素C#很好用,並且易於使用),可以傳遞對Form1實例的引用,該實例要在CodeCompiler的構造函數或ExecuteCode方法中更改。我認爲後者可能會更有意義。

你應該改變你的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(); 
      } 
     } 
    }"; 

然後調用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); 

這將編譯代碼,使Form1實例被調用時可以傳遞給方法。並且對Form1實例的引用在實際爲方法調用傳遞的參數列表中提供(即最後一個參數,即this)。

即使這樣做,它只會允許您執行代碼Form1對象。更重要的是,如果你要沿着這條路走下去,你需要傳遞任何你想要改變的東西到CodeCompiler。我不確定對象objectToChange是否可以工作,而不是formToChange,以及編譯器需要關於您傳遞的對象的多少信息才能在其上執行代碼。

再一次,感覺:你需要一個非常非常獨特的用例來使得任何這些都可以很好地利用時間或理智。 (你必須通過七個精確構造的對象,主要是字符串,到ExecuteCode()每一次你想運行任何東西!)

+0

我希望你不要介意......答案中有一些錯誤,但這是一個很好的開始,並有幾個正確和重要的突出點,我覺得它更有意義,只是建立你已經寫了什麼,而不是添加另一個答案。 – 2014-11-01 21:02:17

+0

@PeterDuniho哦,一點也不 - 我有點尷尬,有多少紅色。但是,考慮到第一次我覺得這樣更容易/更好地添加你的想法,我對此感到滿意! – furkle 2014-11-01 21:04:32

+0

哦,當然沒有!這是很好的答案!謝謝! – IremadzeArchil19910311 2014-11-02 11:57:45

相關問題