在C#中解析命令行参数的最佳方法? [关闭]

本文探讨了多种命令行参数解析库,如PowershellCommandlet、C# CLI、NovellOptions、GenghisCommandLineParser等,以及它们在C#控制台应用程序中的应用。这些库简化了命令行参数的解析过程,提供了丰富的功能,如参数验证、类型转换、布尔标志处理和帮助信息生成。

构建带有参数的控制台应用程序时,可以使用传递给Main(string[] args)

过去,我只是索引/循环该数组,并做了一些正则表达式来提取值。 但是,当命令变得更复杂时,解析可能会变得很丑陋。

所以我对以下内容感兴趣:

  • 您使用的库
  • 您使用的模式

假定命令始终遵循通用标准,例如此处回答


#1楼

Powershell Commandlet。

Powershell会根据命令行小程序上指定的属性进行解析,支持验证,参数集,流水线,错误报告,帮助,以及在其他命令行小程序中使用的所有返回的.NET对象中的最佳功能。

我发现几个链接对入门很有帮助:


#2楼

C#CLI是我编写的一个非常简单的命令行参数解析库。 它是有据可查的开源软件。


#3楼

这是我基于Novell Options类编写的处理程序。

这是针对执行while (input !="exit")样式循环的控制台应用程序的,例如FTP控制台之类的交互式控制台。

用法示例:

static void Main(string[] args)
{
    // Setup
    CommandHandler handler = new CommandHandler();
    CommandOptions options = new CommandOptions();

    // Add some commands. Use the v syntax for passing arguments
    options.Add("show", handler.Show)
        .Add("connect", v => handler.Connect(v))
        .Add("dir", handler.Dir);

    // Read lines
    System.Console.Write(">");
    string input = System.Console.ReadLine();

    while (input != "quit" && input != "exit")
    {
        if (input == "cls" || input == "clear")
        {
            System.Console.Clear();
        }
        else
        {
            if (!string.IsNullOrEmpty(input))
            {
                if (options.Parse(input))
                {
                    System.Console.WriteLine(handler.OutputMessage);
                }
                else
                {
                    System.Console.WriteLine("I didn't understand that command");
                }

            }

        }

        System.Console.Write(">");
        input = System.Console.ReadLine();
    }
}

以及来源:

/// <summary>
/// A class for parsing commands inside a tool. Based on Novell Options class (http://www.ndesk.org/Options).
/// </summary>
public class CommandOptions
{
    private Dictionary<string, Action<string[]>> _actions;
    private Dictionary<string, Action> _actionsNoParams;

    /// <summary>
    /// Initializes a new instance of the <see cref="CommandOptions"/> class.
    /// </summary>
    public CommandOptions()
    {
        _actions = new Dictionary<string, Action<string[]>>();
        _actionsNoParams = new Dictionary<string, Action>();
    }

    /// <summary>
    /// Adds a command option and an action to perform when the command is found.
    /// </summary>
    /// <param name="name">The name of the command.</param>
    /// <param name="action">An action delegate</param>
    /// <returns>The current CommandOptions instance.</returns>
    public CommandOptions Add(string name, Action action)
    {
        _actionsNoParams.Add(name, action);
        return this;
    }

    /// <summary>
    /// Adds a command option and an action (with parameter) to perform when the command is found.
    /// </summary>
    /// <param name="name">The name of the command.</param>
    /// <param name="action">An action delegate that has one parameter - string[] args.</param>
    /// <returns>The current CommandOptions instance.</returns>
    public CommandOptions Add(string name, Action<string[]> action)
    {
        _actions.Add(name, action);
        return this;
    }

    /// <summary>
    /// Parses the text command and calls any actions associated with the command.
    /// </summary>
    /// <param name="command">The text command, e.g "show databases"</param>
    public bool Parse(string command)
    {
        if (command.IndexOf(" ") == -1)
        {
            // No params
            foreach (string key in _actionsNoParams.Keys)
            {
                if (command == key)
                {
                    _actionsNoParams[key].Invoke();
                    return true;
                }
            }
        }
        else
        {
            // Params
            foreach (string key in _actions.Keys)
            {
                if (command.StartsWith(key) && command.Length > key.Length)
                {

                    string options = command.Substring(key.Length);
                    options = options.Trim();
                    string[] parts = options.Split(' ');
                    _actions[key].Invoke(parts);
                    return true;
                }
            }
        }

        return false;
    }
}

#4楼

Genghis Command Line Parser可能有点过时了,但是功能非常完整,对我来说效果很好。


#5楼

我不久前写了一个C#命令行参数解析器。 它位于: http : //www.codeplex.com/CommandLineArguments


#6楼

我强烈建议使用NDesk.Options文档 )和/或Mono.Options (相同的API,不同的名称空间)。 文档中示例

bool show_help = false;
List<string> names = new List<string> ();
int repeat = 1;

var p = new OptionSet () {
    { "n|name=", "the {NAME} of someone to greet.",
       v => names.Add (v) },
    { "r|repeat=", 
       "the number of {TIMES} to repeat the greeting.\n" + 
          "this must be an integer.",
        (int v) => repeat = v },
    { "v", "increase debug message verbosity",
       v => { if (v != null) ++verbosity; } },
    { "h|help",  "show this message and exit", 
       v => show_help = v != null },
};

List<string> extra;
try {
    extra = p.Parse (args);
}
catch (OptionException e) {
    Console.Write ("greet: ");
    Console.WriteLine (e.Message);
    Console.WriteLine ("Try `greet --help' for more information.");
    return;
}

#7楼

有许多解决此问题的方法。 为了完整性和提供替代方案(如果有人希望的话),我在我的google代码库中为两个有用的类添加了这个答案。

第一个是ArgumentList,它仅负责解析命令行参数。 它收集由开关“ / x:y”或“ -x = y”定义的名称/值对,并收集“未命名”条目的列表。 在这里讨论它的基本用法,在这里 查看类

第二部分是CommandInterpreter ,它从.Net类中创建一个功能齐全的命令行应用程序。 举个例子:

using CSharpTest.Net.Commands;
static class Program
{
    static void Main(string[] args)
    {
        new CommandInterpreter(new Commands()).Run(args);
    }
    //example ‘Commands’ class:
    class Commands
    {
        public int SomeValue { get; set; }
        public void DoSomething(string svalue, int ivalue)
        { ... }

使用上面的示例代码,您可以运行以下代码:

Program.exe DoSomething“字符串值” 5

- 要么 -

Program.exe处理/ ivalue = 5 -svalue:“字符串值”

就这么简单,或者像您需要的那样复杂。 您可以查看源代码查看帮助下载二进制文件


#8楼

看起来每个人都有自己的宠物命令行解析器,图我最好也添加我的:)。

http://bizark.codeplex.com/

该库包含一个命令行解析器 ,它将使用命令行中的值初始化一个类。 它具有大量功能(多年来我一直在构建它)。

文档中 ...

BizArk框架中的命令行分析具有以下关键功能:

  • 自动初始化:类属性是根据命令行参数自动设置的。
  • 默认属性:发送一个值而不指定属性名称。
  • 值转换:使用BizArk中也包含的功能强大的ConvertEx类将值转换为正确的类型。
  • 布尔型标志:可以通过简单地使用参数(例如,ex,/ b为true,/ b-为false)或通过添加值true / false,yes / no等来指定标志。
  • 参数数组:只需在命令行名称后添加多个值即可设置定义为数组的属性。 例如,/ x 1 2 3将用数组{1,2,3}填充x(假设x被定义为整数数组)。
  • 命令行别名:一个属性可以支持多个命令行别名。 例如,帮助使用别名?。
  • 部分名称识别:您无需拼写全名或别名,只需拼写足够多的语法分析器即可将属性/别名与其他属性区分开。
  • 支持ClickOnce:即使在URL中为已部署的ClickOnce应用程序将其指定为查询字符串,也可以初始化属性。 命令行初始化方法将检测它是否以ClickOnce的形式运行,因此使用它时无需更改代码。
  • 自动创建/? 帮助:这包括考虑到控制台宽度的良好格式。
  • 将命令行参数加载/保存到文件:如果您要运行多个大型,复杂的命令行参数集,则此功能特别有用。

#9楼

我建议使用开源库CSharpOptParse 。 它解析命令行,并使用命令行输入合并用户定义的.NET对象。 在编写C#控制台应用程序时,我总是转向该库。


#10楼

看看http://github.com/mono/mono/tree/master/mcs/class/Mono.Options/


#11楼

我喜欢那个 ,因为您可以为参数“定义规则”,无论是否需要,...

或者如果您是Unix人士,那么您可能会喜欢GNU Getopt .NET端口。


#12楼

WPF TestApi库带有用于C#开发的最好的命令行解析器之一。 我强烈建议您从Ivo Manolov的API博客中进行调查

// EXAMPLE #2:
// Sample for parsing the following command-line:
// Test.exe /verbose /runId=10
// This sample declares a class in which the strongly-
// typed arguments are populated
public class CommandLineArguments
{
   bool? Verbose { get; set; }
   int? RunId { get; set; }
}

CommandLineArguments a = new CommandLineArguments();
CommandLineParser.ParseArguments(args, a);

#13楼

我个人最喜欢的是Peter Palotas的http://www.codeproject.com/KB/recipes/plossum_commandline.aspx

[CommandLineManager(ApplicationName="Hello World",
    Copyright="Copyright (c) Peter Palotas")]
class Options
{
   [CommandLineOption(Description="Displays this help text")]
   public bool Help = false;

   [CommandLineOption(Description = "Specifies the input file", MinOccurs=1)]
   public string Name
   {
      get { return mName; }
      set
      {
         if (String.IsNullOrEmpty(value))
            throw new InvalidOptionValueException(
                "The name must not be empty", false);
         mName = value;
      }
   }

   private string mName;
}

#14楼

我最近遇到了我非常喜欢的FubuCore命令行解析实现,原因是:

  • 它很容易使用-尽管我找不到它的文档,但是FubuCore解决方案还提供了一个项目,其中包含一组不错的单元测试,这些单元测试比任何文档都讲更多的功能。
  • 它具有出色的面向对象设计,没有代码重复或我以前在命令行解析应用中拥有的其他类似内容
  • 这是声明性的:您基本上是为Commands和参数集编写类,并用属性装饰它们以设置各种选项(例如,名称,描述,强制/可选)
  • 库甚至根据这些定义打印出一个不错的使用图

以下是有关如何使用此功能的简单示例。 为了说明这种用法,我编写了一个简单的实用程序,其中包含两个命令:-添加(将对象添加到列表中-对象由名称(字符串),值(int)和布尔值标志组成)-列表(列表所有当前添加的对象)

首先,我为“ add”命令编写了一个Command类:

[Usage("add", "Adds an object to the list")]
[CommandDescription("Add object", Name = "add")]
public class AddCommand : FubuCommand<CommandInput>
{
    public override bool Execute(CommandInput input)
    {
        State.Objects.Add(input); // add the new object to an in-memory collection

        return true;
    }
}

此命令将CommandInput实例作为参数,因此接下来我将其定义为:

public class CommandInput
{
    [RequiredUsage("add"), Description("The name of the object to add")]
    public string ObjectName { get; set; }

    [ValidUsage("add")]
    [Description("The value of the object to add")]
    public int ObjectValue { get; set; }

    [Description("Multiply the value by -1")]
    [ValidUsage("add")]
    [FlagAlias("nv")]
    public bool NegateValueFlag { get; set; }
}

下一个命令是“列表”,其实现方式如下:

[Usage("list", "List the objects we have so far")]
[CommandDescription("List objects", Name = "list")]
public class ListCommand : FubuCommand<NullInput>
{
    public override bool Execute(NullInput input)
    {
        State.Objects.ForEach(Console.WriteLine);

        return false;
    }
}

'list'命令不带任何参数,因此我为此定义了一个NullInput类:

public class NullInput { }

现在剩下的就是将它连接到Main()方法中,如下所示:

    static void Main(string[] args)
    {
        var factory = new CommandFactory();
        factory.RegisterCommands(typeof(Program).Assembly);

        var executor = new CommandExecutor(factory);

        executor.Execute(args);
    }

该程序按预期工作,在任何命令无效的情况下,会打印有关正确用法的提示:

  ------------------------
    Available commands:
  ------------------------
     add -> Add object
    list -> List objects
  ------------------------

以及“ add”命令的示例用法:

Usages for 'add' (Add object)
  add <objectname> [-nv]

  -------------------------------------------------
    Arguments
  -------------------------------------------------
     objectname -> The name of the object to add
    objectvalue -> The value of the object to add
  -------------------------------------------------

  -------------------------------------
    Flags
  -------------------------------------
    [-nv] -> Multiply the value by -1
  -------------------------------------

#15楼

请使用apache commons cli API的.net端口。 这很好。

http://sourceforge.net/projects/dotnetcli/

以及用于概念和介绍的原始API

http://commons.apache.org/cli/


#16楼

一个非常简单易用的临时类,用于命令行解析,它支持默认参数。

class CommandLineArgs
{
    public static CommandLineArgs I
    {
        get
        {
            return m_instance;
        }
    }

    public  string argAsString( string argName )
    {
        if (m_args.ContainsKey(argName)) {
            return m_args[argName];
        }
        else return "";
    }

    public long argAsLong(string argName)
    {
        if (m_args.ContainsKey(argName))
        {
            return Convert.ToInt64(m_args[argName]);
        }
        else return 0;
    }

    public double argAsDouble(string argName)
    {
        if (m_args.ContainsKey(argName))
        {
            return Convert.ToDouble(m_args[argName]);
        }
        else return 0;
    }

    public void parseArgs(string[] args, string defaultArgs )
    {
        m_args = new Dictionary<string, string>();
        parseDefaults(defaultArgs );

        foreach (string arg in args)
        {
            string[] words = arg.Split('=');
            m_args[words[0]] = words[1];
        }
    }

    private void parseDefaults(string defaultArgs )
    {
        if ( defaultArgs == "" ) return;
        string[] args = defaultArgs.Split(';');

        foreach (string arg in args)
        {
            string[] words = arg.Split('=');
            m_args[words[0]] = words[1];
        }
    }

    private Dictionary<string, string> m_args = null;
    static readonly CommandLineArgs m_instance = new CommandLineArgs();
}

class Program
{
    static void Main(string[] args)
    {
        CommandLineArgs.I.parseArgs(args, "myStringArg=defaultVal;someLong=12");
        Console.WriteLine("Arg myStringArg  : '{0}' ", CommandLineArgs.I.argAsString("myStringArg"));
        Console.WriteLine("Arg someLong     : '{0}' ", CommandLineArgs.I.argAsLong("someLong"));
    }
}

#17楼

我真的很喜欢命令行解析器库( http://commandline.codeplex.com/ )。 它具有通过属性设置参数的非常简单而优雅的方法:

class Options
{
    [Option("i", "input", Required = true, HelpText = "Input file to read.")]
    public string InputFile { get; set; }

    [Option(null, "length", HelpText = "The maximum number of bytes to process.")]
    public int MaximumLenght { get; set; }

    [Option("v", null, HelpText = "Print details during execution.")]
    public bool Verbose { get; set; }

    [HelpOption(HelpText = "Display this help screen.")]
    public string GetUsage()
    {
        var usage = new StringBuilder();
        usage.AppendLine("Quickstart Application 1.0");
        usage.AppendLine("Read user manual for usage instructions...");
        return usage.ToString();
    }
}

#18楼

http://www.codeplex.com/commonlibrarynet上有一个命令行参数解析器

它可以使用解析参数
1.属性
2.明确的电话
3.多参数或字符串数​​组的单行

它可以处理以下内容:

- 配置 :QA - STARTDATE:$ {} 今天 - 区域 : '纽约' Settings01

它很容易使用。


#19楼

你可能会喜欢我的一个地毯

易于使用且可扩展的命令行参数解析器。 句柄:布尔,加号/减号,字符串,字符串列表,CSV,枚举。

内建'/?' 帮助模式。

内建'/ ??' 和'/?D'文档生成器模式。

static void Main(string[] args) 
{            
    // create the argument parser
    ArgumentParser parser = new ArgumentParser("ArgumentExample", "Example of argument parsing");

    // create the argument for a string
    StringArgument StringArg = new StringArgument("String", "Example string argument", "This argument demonstrates string arguments");

    // add the argument to the parser 
    parser.Add("/", "String", StringArg);

    // parse arguemnts
    parser.Parse(args);

    // did the parser detect a /? argument 
    if (parser.HelpMode == false) 
    {
        // was the string argument defined 
        if (StringArg.Defined == true)
        {
            // write its value
            RC.WriteLine("String argument was defined");
            RC.WriteLine(StringArg.Value);
        }
    }
}

编辑:这是我的项目,因此,此答案不应被视为第三方的认可。 就是说,我确实将它用于我编写的每个基于命令行的程序,它是开源的,我希望其他人可以从中受益。


#20楼

CLAP (命令行参数解析器)具有可用的API,并且文档详尽。 您可以制作一个方法,并为参数添加注释。 https://github.com/adrianaisemberg/CLAP

<think> 我们需要在C#中嵌入Python脚本,并传递参数给Python脚本。这通常可以通过两种方式实现: 1. 使用IronPython(.NET平台上的Python实现) 2. 使用进程调用(如启动Python解释器进程并传递参数) 由于IronPython对Python3的支持有限,且可能不支持所有第三方库,因此这里我们介绍第二种更通用的方法:通过命令行参数传递。 步骤: 1. 准备一个Python脚本,该脚本可以接收命令行参数。 2.C#中,使用Process类启动Python进程,并传递参数。 3. 获取Python脚本的输出(如果需要)。 下面是一个完整的示例: 假设我们有一个Python脚本 `script.py`,它接受两个数字参数并返回它们的和。 **Python脚本 (script.py):** ```python import sys if __name__ == "__main__": # 获取命令行参数,注意第一个参数是脚本名 num1 = float(sys.argv[1]) num2 = float(sys.argv[2]) result = num1 + num2 print(result) # 将结果打印到标准输出 ``` **C# 代码调用Python脚本并传递参数:** ```csharp using System; using System.Diagnostics; public class PythonInterop { public static void Main() { // Python解释器路径(如果已在环境变量中,可以直接写"python"或"python3") string pythonPath = "python"; // Python脚本路径 string scriptPath = @"C:\path\to\script.py"; // 要传递的参数 double arg1 = 5.5; double arg2 = 4.5; // 创建进程启动信息 ProcessStartInfo start = new ProcessStartInfo { FileName = pythonPath, Arguments = $"{scriptPath} {arg1} {arg2}", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true }; // 启动进程 using (Process process = Process.Start(start)) { // 从标准输出读取结果 using (System.IO.StreamReader reader = process.StandardOutput) { string result = reader.ReadToEnd(); Console.WriteLine($"Result: {result}"); } // 读取错误信息(如果有) string errors = process.StandardError.ReadToEnd(); if (!string.IsNullOrEmpty(errors)) { Console.WriteLine($"Errors: {errors}"); } process.WaitForExit(); } } } ``` 解释: 1. **ProcessStartInfo**:配置进程启动信息,包括要执行的程序(python解释器)和参数(脚本路径和两个数字)。 2. **RedirectStandardOutput**:设置为true,以便我们可以捕获Python脚本的输出。 3. **RedirectStandardError**:设置为true,以便捕获错误信息。 4. **CreateNoWindow**:设置为true,不显示控制台窗口(后台运行)。 5. 通过`StandardOutput`读取Python脚本打印到标准输出的内容(即我们的结果)。 6. 通过`StandardError`读取错误信息。 注意事项: - 确保Python脚本的路径正确,且Python环境已安装并配置到环境变量(或者直接指定python解释器的完整路径)。 - 传递的参数之间用空格分隔,Python脚本中通过`sys.argv`获取。 - 如果参数包含空格或特殊字符,需要转义或用引号括起来。 另一种方法:使用标准输入传递参数(适用于大量数据或复杂结构) 如果参数复杂或数据量大,可以通过标准输入传递(例如JSON格式): **Python脚本 (script_stdin.py):** ```python import sys import json if __name__ == "__main__": # 从标准输入读取 data = sys.stdin.read() # 解析为JSON data_dict = json.loads(data) num1 = data_dict['num1'] num2 = data_dict['num2'] result = num1 + num2 print(json.dumps({'result': result})) ``` **C# 代码(使用标准输入传递JSON):** ```csharp using System; using System.Diagnostics; using System.IO; using System.Text; using Newtonsoft.Json; // 需要安装Newtonsoft.Json包 public class PythonInteropWithStdin { public static void Main() { string pythonPath = "python"; string scriptPath = @"C:\path\to\script_stdin.py"; // 创建要传递的数据 var data = new { num1 = 5.5, num2 = 4.5 }; string jsonData = JsonConvert.SerializeObject(data); ProcessStartInfo start = new ProcessStartInfo { FileName = pythonPath, Arguments = scriptPath, UseShellExecute = false, RedirectStandardInput = true, // 重定向标准输入 RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true }; using (Process process = Process.Start(start)) { // 写入标准输入 using (StreamWriter writer = process.StandardInput) { writer.Write(jsonData); writer.Close(); // 必须关闭,否则Python脚本会一直等待输入结束 } string result = process.StandardOutput.ReadToEnd(); string errors = process.StandardError.ReadToEnd(); process.WaitForExit(); if (string.IsNullOrEmpty(errors)) { // 解析JSON结果 dynamic output = JsonConvert.DeserializeObject(result); Console.WriteLine($"Result: {output.result}"); } else { Console.WriteLine($"Errors: {errors}"); } } } } ``` 这种方法更灵活,可以传递复杂结构的数据。 总结: 根据需求选择合适的方法- 少量简单参数:命令行参数 - 复杂结构或大数据:标准输入(JSON或其他格式) §§相关问题§§: 1. 如何安全地处理从Python脚本返回的数据? 2.C#中嵌入Python时,如何处理Python环境的依赖问题? 3. 除了进程调用,还有哪些方法可以实现C#与Python的互操作? 4. 如何提高C#与Python之间数据传递的性能? </think> # C# 嵌入 Python 的参数传递方案 在 C# 中嵌入 Python 并传递参数可以通过多种方式实现,下面我将详细介绍三种主流方法,并提供完整代码示例。 ## 方案一:使用 IronPython (推荐) IronPython 是 .NET 平台上的 Python 实现,可以直接在 C# 中调用 Python 代码。 ```csharp using IronPython.Hosting; using Microsoft.Scripting.Hosting; using System.Collections.Generic; public class PythonInterop { public static void ExecutePythonWithParameters() { // 创建 Python 引擎 var engine = Python.CreateEngine(); ScriptScope scope = engine.CreateScope(); // 准备要传递的参数 string name = "John"; int age = 30; List<double> data = new List<double> { 1.2, 3.4, 5.6 }; Dictionary<string, object> complexData = new Dictionary<string, object> { {"id", 1001}, {"values", new double[] {7.8, 9.0}} }; // 将参数设置到 Python 作用域 scope.SetVariable("name", name); scope.SetVariable("age", age); scope.SetVariable("data_list", data); scope.SetVariable("complex_data", complexData); // 执行的 Python 代码 string pythonCode = @" def process_data(name, age, data_list, complex_data): print(f'Name: {name}, Age: {age}') print(f'Data list: {data_list}') print(f'Complex data: {complex_data}') # 处理数据 total = sum(data_list) + complex_data['id'] processed = [x * 2 for x in data_list] # 返回结果 return { 'total': total, 'processed': processed, 'message': f'Processed for {name}' } # 调用函数并返回结果 result = process_data(name, age, data_list, complex_data) "; // 执行 Python 代码 ScriptSource source = engine.CreateScriptSourceFromString(pythonCode); source.Execute(scope); // 获取 Python 返回值 dynamic result = scope.GetVariable("result"); // 使用返回的数据 double total = result.total; List<double> processed = result.processed; string message = result.message; Console.WriteLine($"Total: {total}"); Console.WriteLine($"Processed: {string.Join(", ", processed)}"); Console.WriteLine($"Message: {message}"); } } ``` ### 关键点说明: 1. **变量传递**:使用 `SetVariable` 方法C# 变量传递到 Python 作用域 2. **数据类型映射**: - 基本类型自动转换 (int, double, string) - 集合类型自动转换 (List ↔ list, Dictionary ↔ dict) 3. **返回值处理**:Python 返回值可通过 `dynamic` 类型在 C# 中访问 4. **执行流程**:Python 代码作为字符串执行,与 C# 共享内存空间 ## 方案二:使用 Python.NET Python.NET 提供更直接的 Python/C# 互操作,支持调用 CPython 解释器。 ```csharp using Python.Runtime; using System; using System.Collections.Generic; public class PythonNetInterop { public static void CallPythonWithParameters() { // 初始化 Python 运行时 Runtime.PythonDLL = @"path\to\python39.dll"; // 替换为实际路径 PythonEngine.Initialize(); using (Py.GIL()) // 获取 Python 全局解释器锁 { // 创建 Python 模块 dynamic np = Py.Import("numpy"); dynamic sys = Py.Import("sys"); // 准备参数 string name = "Alice"; int age = 25; double[] data = new double[] { 1.1, 2.2, 3.3 }; // 将 C# 对象转换为 Python 对象 PyObject pyName = new PyString(name); PyObject pyAge = new PyInt(age); PyObject pyData = data.ToPython(); // 使用扩展方法 // 调用 Python 函数 dynamic math = Py.Import("math"); double sqrtResult = math.sqrt(144); // 调用内置函数 // 调用自定义函数 string pythonCode = @" def process_data(name, age, data): import numpy as np avg = np.mean(data) return f'{name} ({age}) - Average: {avg}' "; dynamic scope = Py.CreateScope(); scope.Exec(pythonCode); dynamic processData = scope.Get("process_data"); // 传递参数调用函数 PyObject result = processData(pyName, pyAge, pyData); string output = result.As<string>(); Console.WriteLine($"Python output: {output}"); Console.WriteLine($"Sqrt result: {sqrtResult}"); } // 关闭 Python 引擎 PythonEngine.Shutdown(); } } // 辅助扩展方法 public static class PythonExtensions { public static PyObject ToPython(this object obj) { switch (obj) { case null: return new PyNone(); case string s: return new PyString(s); case int i: return new PyInt(i); case double d: return new PyFloat(d); case Array array: return array.ToPythonArray(); default: throw new NotSupportedException($"Type not supported: {obj.GetType()}"); } } private static PyObject ToPythonArray(this Array array) { dynamic np = Py.Import("numpy"); return np.array(array); } } ``` ## 方案三:通过命令行参数传递 对于简单的脚本调用,可以通过命令行参数传递数据。 ```csharp using System; using System.Diagnostics; using System.Text.Json; public class PythonProcessCaller { public static void ExecutePythonScript() { // 准备参数对象 var parameters = new { Name = "Bob", Age = 35, Scores = new double[] { 85.5, 92.0, 78.5 } }; // 序列化为 JSON string jsonArgs = JsonSerializer.Serialize(parameters); // 配置 Python 进程 var startInfo = new ProcessStartInfo { FileName = "python", // 或完整路径 Arguments = $"process_data.py \"{jsonArgs.Replace("\"", "\\\"")}\"", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true }; // 启动进程 using var process = new Process { StartInfo = startInfo }; process.Start(); // 获取输出 string output = process.StandardOutput.ReadToEnd(); string errors = process.StandardError.ReadToEnd(); process.WaitForExit(); if (!string.IsNullOrEmpty(errors)) { Console.WriteLine($"Python error: {errors}"); } else { Console.WriteLine($"Python output: {output}"); // 反序列化结果 var result = JsonSerializer.Deserialize<Dictionary<string, object>>(output); Console.WriteLine($"Average score: {result["average"]}"); } } } ``` ### 对应的 Python 脚本 (process_data.py) ```python import sys import json if __name__ == "__main__": # 获取命令行参数 json_args = sys.argv[1] data = json.loads(json_args) name = data["Name"] age = data["Age"] scores = data["Scores"] # 计算平均值 average = sum(scores) / len(scores) # 构建结果 result = { "name": name, "age": age, "average": average, "status": "success" } # 输出 JSON 结果 print(json.dumps(result)) ``` ## 方法对比 | 方法 | 优点 | 缺点 | 适用场景 | |------|------|------|---------| | **IronPython** | 无需外部依赖,直接.NET集成,高性能 | 不支持所有Python库 | 简单脚本,纯Python代码 | | **Python.NET** | 支持完整CPython生态系统 | 需要管理Python环境,配置复杂 | 需要第三方库(NumPy等) | | **命令行调用** | 独立进程,隔离性好 | 性能开销大,参数传递复杂 | 完整Python应用集成 | ## 最佳实践建议 1. **数据传递**: - 基本类型直接传递 - 复杂对象使用 JSON 序列化 - 大数据集使用共享内存或文件交换 2. **错误处理**: ```csharp try { // Python 调用代码 } catch (IronPython.Runtime.Exceptions.PythonException ex) { Console.WriteLine($"Python error: {ex.Message}"); Console.WriteLine($"Traceback: {ex.FormatStackTrace()}"); } ``` 3. **性能优化**: - 避免频繁创建销毁 Python 引擎 - 使用缓存复用 Python 对象 - 批量处理数据减少交互次数 4. **环境管理**: - 使用虚拟环境确保依赖一致性 - 指定 Python 解释器路径 - 打包 Python 环境与应用程序
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值