最小,良好的公民,C#控制台应用程序样板

时间:2009-10-27 14:48:09

标签: c# command-line

C#控制台应用程序入口点的 minimal 样板代码是什么?它将使行为良好的公民?

当有人使用Visual Studio创建控制台应用程序项目时(截至撰写本文时为2008年),您将看到一个样板Program.cs,如下所示:

class Program
{
    static void Main(string[] args)
    {
    }
}

然而,每个人都需要做一些事情来使控制台应用程序成为最低好公民。例如,如果发生异常,则将清除消息写入标准错误(Console.Error)而不是标准输出(Console.Out)。同样,如果发生某些错误,请将错误代码设置为非零值,以便调用进程可以检测到失败。

C#控制台应用程序入口点的 minimal 样板代码是什么?它将成为行为良好的公民?您将添加或更改以下内容?

using System;
using System.Diagnostics;
using System.Linq;

static class Program
{
    static void Run(string[] args)
    {
        // TODO Replace line-echoing sample with actual application code

        string line;
        while ((line = Console.ReadLine()) != null)
            Console.WriteLine(line);
    }

    static int Main(string[] args)
    {
        // TODO Use a more robust arguments parser
        if (args.Any(arg => arg.Equals("/v") || arg.Equals("-v"))) // verbose?
            Trace.Listeners.Add(new ConsoleTraceListener(true));

        try
        {
            Run(args);
            return Environment.ExitCode;
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
            Trace.TraceError(e.ToString());

            return Environment.ExitCode != 0
                 ? Environment.ExitCode : 100;
        }
    }
}

这个样板实现了什么:

  • 如果抛出异常:
    • 打印一条干净的信息至标准错误
    • 跟踪完整的详细信息
    • 除非已设置
    • ,否则退出代码将设置为任意非零值(100)
  • 可以通过详细开关
  • 动态启用跟踪
  • 跟踪被发送到标准错误,不会干扰实际输出
  • 成功完成后,退出代码会反映最后一个Environment.ExitCode值,该值通常为零但可以通过下游代码更改
  • Program class is static

此问题的非目标:

  • 识别处理代码或库的命令行参数
  • 识别外部日志记录或跟踪库

4 个答案:

答案 0 :(得分:7)

我认为一切都取决于该应用的实际要求。如果没有要求进行自定义错误处理 - 请不要这样做。如果没有程序需要检查您的应用程序的退出代码 - 无需返回它;我认为有些情况不适用这些要求。

换句话说,最小化是最小的;做最可能有效的事情。如果你的应用程序符合要求,我想我们可以称之为良好的行为。

答案 1 :(得分:3)

对我来说,我希望看到Run(args)替换为类的实例化。类似的东西:

主要尝试:

try
{
    // Process args to get parameters for AClass
    AClass program = new AClass(a, b);
    return program.Run();
}

<强> ACLASS:

public class AClass {
    AClass(string a, string b) { ... }
    public int Run() {
        ...
        return Environment.ExitCode;
    }
}

这样的事情会阻碍程序代码并鼓励创建面向对象的方法。

关于AClass()构造函数,我认为参数应该在传递到AClass()之前进行处理,而不是AClass()必须知道它是通过控制台应用程序创建的。< / p>

答案 2 :(得分:2)

关于这个问题,我发现这篇文章是最深入和最有用的:

.NET中的控制台应用程序,或者教授新的老狗伎俩

迈克尔布鲁克

MSDN Magazine 2004年2月

http://msdn.microsoft.com/en-us/magazine/cc164014.aspx

答案 3 :(得分:0)

我使用emacs和名为defaultcontent.el的东西。如果您不熟悉,Emacs是一个文本编辑器,并且非常易于扩展。 defaultcontent.el是一个扩展,它将默认内容插入(惊讶)新文件,当它们由emacs首次创建时。

因此,如果我尝试打开一个不存在的.cs文件,emacs会创建它,然后将一堆默认内容放入其中。我决定在新文件中包含哪些内容。对于我的C#文件,此内容包括:

  • 带有时间戳,版权,其他样板的简单文档。
  • 一堆程序集范围属性(FileVersion,AssemblyDescription等)
  • 单一课程
  • 一个构造函数,它接受一个字符串参数数组并解析它们;解析逻辑是一个switch语句,带有一些用于解析整数,字符串和布尔值的样板。
  • 一种使用方法,用于打印使用信息。
  • 一个Main方法,它实例化类并调用“Run()”;这是在try..catch中包围的,在catch中,调用了Usage()方法。如果程序中的任何位置发生任何错误,则会显示用法消息。

defaultcontent.el还允许我将光标定位在我想要的位置。就我而言,那是在空的Run()方法的中间。

这是我的默认内容:

// default.cs
// ------------------------------------------------------------------
//
// Description goes here....
// 
// Author: MyUser
// built on host: MyMachine
// Created Tue Oct 27 15:01:18 2009
//
// last saved: 
// Time-stamp: <2009-October-20 00:18:52>
// ------------------------------------------------------------------
//
// Copyright Notice here
// All rights reserved!
//
// ------------------------------------------------------------------

using System;
using System.Reflection;


// to allow fast ngen
[assembly: AssemblyTitle("default.cs")]
[assembly: AssemblyDescription("insert purpose here")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Dino Chiesa")]
[assembly: AssemblyProduct("Tools")]
[assembly: AssemblyCopyright("Copyright notice again")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.1.1.1")]


namespace Cheeso.ToolsAndTests
{

  public class default
  {
    // ctor
    public default () {}

    string _positionalParam1;
    string _param1;
    int _intParam = DefaultIntParamValue;
    bool _flag1;

    private readonly int DefaultIntParamValue = -99;

    // ctor
    public default (string[] args)
    {
        for (int i=0; i < args.Length; i++)
        {
            switch (args[i])
            {
            case "-stringArg":
                i++;
                if (args.Length <= i) throw new ArgumentException(args[i]);
                _param1 = args[i];
                break;

            case "-intArg":
                i++;
                if (args.Length <= i) throw new ArgumentException(args[i]);
                if (_intParam != DefaultIntParamValue)
                    throw new ArgumentException(args[i]);
                if (args[i].StartsWith("0x"))
                    _intParam = System.Int32.Parse(args[i].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier );
                else
                    _intParam = System.Int32.Parse(args[i]);
                break;


            case "-boolarg":
                _flag1 = true;
                break;

            case "-?":
                throw new ArgumentException(args[i]);

            default:
                if (_positionalParam1 != null)
                    throw new ArgumentException(args[i]);

                _positionalParam1 = args[i];
                break;
            }
        }

        // default values
        if (_positionalParam1 == null)
            _positionalParam1 = "default.value.for.positional.param";

        if (_param1 == null)
            _param1 = "default.value.for.param1";

        if (_param2 == 0)
            _param2 = DEFAULT_value_for_param2;

    }

    public void Run()
    {



    }

    public static void Usage()
    {
        Console.WriteLine("\ndefault: <usage statement here>.\n");
        Console.WriteLine("Usage:\n  default [-arg1 <value>] [-arg2]");
    }


    public static void Main(string[] args)
    {
      try 
      {
        new default(args)
            .Run();
      }
      catch (System.Exception exc1)
      {
        Console.WriteLine("Exception: {0}", exc1.ToString());
        Usage();
      }
    }

  }
}

我还为.c,.cpp,.vb,.csproj,.xml,.xsd,.wsdl,makefile和许多其他文件类型设置了defaultcontent。