簡體   English   中英

從 C# 中的文本文件執行代碼行

[英]Execute code lines from a text file in C#

我有一個文本文件,如下所示:

AssembleComponent Motor = new AssembleComponent;
AssembleComponent Shaft = new AssembleComponent;
......

Motor.cost = 100;
Motor.quantity = 100;
Shaft.cost = 10;
Shaft.quantity = 100;
......

我希望在 C# 中執行這些代碼行,以便將這些 Motor.cost、Motor.quantity、Shaft.cost、Shaft.quantity 變量存儲在 memory 中以供以后計算。

我該怎么做才能實現這一目標?

而是將其存儲為XML

<?xml version="1.0" encoding="UTF-8"?>
<Components>
    <Component name="Motor" cost="100" quantity="100" />
    <Component name="Shaft" cost="10" quantity="100" />
</Components>

假設您有這個定義

public class AssembleComponent
{
    public decimal Cost { get; set; }
    public int Quantity { get; set; }
}

像這樣加載

var components = new Dictionary<string, AssembleComponent>();
XDocument doc = XDocument.Load(@"C:\Users\Oli\Desktop\components.xml");
foreach (XElement el in doc.Root.Descendants()) {
    string name = el.Attribute("name").Value;
    decimal cost = Decimal.Parse(el.Attribute("cost").Value);
    int quantity = Int32.Parse(el.Attribute("quantity").Value);
    components.Add(name, new AssembleComponent{ 
                             Cost = cost, Quantity = quantity
                         });
}

然后,您可以像這樣訪問組件

AssembleComponent motor = components["Motor"];
AssembleComponent shaft = components["Shaft"];

注意:在運行時通過調用編譯器動態創建變量名不是很有用,因為您需要在編譯時(或設計時,如果願意)了解它們,以對它們進行有用的處理。 因此,我將組件添加到字典中。 這是動態創建“變量”的好方法。

您可以使用Microsoft.CSharp.CSharpCodeProvider即時編譯代碼。

具體來說,看看CompileAssemblyFromFile

如果只是數據,請不要使用純文本文件,而應使用XML代替。

您可以將XML反序列化為對象並對其執行必要的操作。

這是我過去使用過的一些代碼,可以滿足您的大部分需求,盡管您可能需要對其進行調整以適應您的特定需求。 簡而言之,它執行以下操作:

  • 在該名稱空間中創建一個臨時名稱空間和一個公共靜態方法。
  • 將代碼編譯為內存中的程序集。
  • 提取編譯的方法並將其轉換為委托。
  • 執行委托。

那時,這就像執行普通的靜態方法一樣,因此當您說希望將結果保存在內存中以備后用時,您必須弄清楚它是如何工作的。

public void CompileAndExecute(string CodeBody)
{
    // Create the compile unit
    CodeCompileUnit ccu = CreateCode(CodeBody);

    // Compile the code 
    CompilerParameters comp_params = new CompilerParameters();
    comp_params.GenerateExecutable = false;
    comp_params.GenerateInMemory = true;
    comp_params.TreatWarningsAsErrors = true;
    comp_results = code_provider.CompileAssemblyFromDom(comp_params, ccu);

    // CHECK COMPILATION RESULTS
    if (!comp_results.Errors.HasErrors)
    {
        Type output_class_type = comp_results.CompiledAssembly.GetType("TestNamespace.TestClass");

        if (output_class_type != null)    
        {    
            MethodInfo compiled_method = output_class_type.GetMethod("TestMethod", BindingFlags.Static | BindingFlags.Public);    
            if (compiled_method != null)    
            {    
                Delgate created_delegate = Delegate.CreateDelegate(typeof(System.Windows.Forms.MethodInvoker), compiled_method);
                if (created_delegate != null)
                {
                    // Run the code
                    created_delegate.DynamicInvoke();
                }
            }
        }
    }
    else
    {
        foreach (CompilerError error in comp_results.Errors)
        {
            // report the error
        }
    }
}

public CodeCompileUnit CreateCode(string CodeBody)
{
    CodeNamespace code_namespace = new CodeNamespace("TestNamespace");

    // add the class to the namespace, add using statements
    CodeTypeDeclaration code_class = new CodeTypeDeclaration("TestClass");
    code_namespace.Types.Add(code_class);
    code_namespace.Imports.Add(new CodeNamespaceImport("System"));

    // set function details
    CodeMemberMethod method = new CodeMemberMethod();
    method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
    method.ReturnType = new CodeTypeReference(typeof(void));
    method.Name = "TestMethod";

    // add the user typed code
    method.Statements.Add(new CodeSnippetExpression(CodeBody));

    // add the method to the class
    code_class.Members.Add(method);

    // create a CodeCompileUnit to pass to our compiler
    CodeCompileUnit ccu = new CodeCompileUnit();
    ccu.Namespaces.Add(code_namespace);

    return ccu;
}

您有兩個主要選擇:

  1. 展開文本,直到它變成有效的C#代碼,然后編譯並執行
  2. 解析並自己執行(即解釋)。

這可以按照以下步驟完成: CodeGeneration => InMemory Compilation to Exe ==> Execution

您可以設計類似於這樣的結構:

public bool RunMain(string code)
{
    const string CODE_NAMESPACE = "CompileOnFly";
    const string CODE_CLASS = "Program";
    const string CODE_METHOD = "Main";

    try
    {
        var code_namespace = new CodeNamespace(CODE_NAMESPACE);

        // add the class to the namespace, add using statements
        var  code_class = new CodeTypeDeclaration(CODE_CLASS);
        code_namespace.Types.Add(code_class);
        code_namespace.Imports.Add(new CodeNamespaceImport("System"));

        // set function details
        var method = new CodeMemberMethod();
        method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
        method.ReturnType = new CodeTypeReference(typeof(void));
        method.Name = CODE_METHOD;

        // add the user typed code
        method.Statements.Add(new CodeSnippetExpression(code));

        // add the method to the class
        code_class.Members.Add(method);

        // create a CodeCompileUnit to pass to our compiler
        CodeCompileUnit code_compileUnit = new CodeCompileUnit();
        code_compileUnit.Namespaces.Add(code_namespace);


        var compilerParameters = new CompilerParameters();
        compilerParameters.ReferencedAssemblies.Add("system.dll");
        compilerParameters.GenerateExecutable = true;
        compilerParameters.GenerateInMemory = true;
        compilerParameters.TreatWarningsAsErrors = true;

        var code_provider = CodeDomProvider.CreateProvider("CSharp");
        var comp_results = code_provider.CompileAssemblyFromDom(compilerParameters, code_compileUnit);

        if (comp_results.Errors.HasErrors)
        {
            StringBuilder sb = new StringBuilder();
            foreach (CompilerError error in comp_results.Errors)
            {
                sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
            }

                   
            throw new InvalidOperationException(sb.ToString());
        }

        //Get assembly, type and the Main method:
        Assembly assembly = comp_results.CompiledAssembly;
        Type program = assembly.GetType($"{CODE_NAMESPACE}.{CODE_CLASS}");
        MethodInfo main = program.GetMethod(CODE_METHOD);

        //runtit
        main.Invoke(null, null);
        return true;

    }
    catch(Exception compileException)
    {
        Console.Write(compileException.ToString());
        return false;
    }
} 

在上面的代碼中,我們實際上創建了一個簡單的控制台Program.Main()作為

namespace CompileOnFly
{
    internal class Program
    {
       static void Main()
        {
            //<your code here>  
        }
    }
}

在 memory 中,然后在 Memory 中將其編譯為可執行文件並執行。 但是Main()主體//<your code here>是通過參數code動態添加到方法中的。

因此,如果您在文本文件script.txt中有一個腳本,如下所示:

Console.Write("Write your name: ");
var name = Console.ReadLine();
Console.WriteLine("Happy new year 2023 " + name);

您可以簡單地閱讀所有文本並將其作為參數發送給它:

 var code = File.ReadAllText(@"script.txt");
 RunMain(code);

運行script.txt文件中的語句。


暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM