Подтвердить что ты не робот

Минимальный, хороший гражданин, С# приложение приложения консоли

Каким будет минимальный код шаблона для входной точки приложения консоли С#, который сделает его хорошим человеком?

Когда кто-то выходит, чтобы создать проект Консольное приложение с помощью Visual Studio (до 2008 года на момент написания), вам будет представлен шаблон Program.cs, который выглядит следующим образом:

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

Есть, однако, несколько вещей, которые каждый должен сделать, чтобы сделать консольное приложение минимально хорошим гражданином. Например, если возникает исключение, выпишите чистое сообщение в стандартную ошибку (Console.Error), а не стандартный вывод (Console.Out). Аналогично, установите код ошибки в ненулевое значение в случае некоторой ошибки, чтобы вызывающие процессы могли обнаруживать сбои.

Каким будет минимальный код шаблона для входной точки приложения консоли С#, который сделает его хорошим человеком? Что бы вы добавили или изменили следующее?

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 класс статический

Без цели этого вопроса:

  • Идентифицировать аргументы командной строки, обрабатывающие код или библиотеку
  • Определите внешнюю библиотеку ведения журнала или трассировки
4b9b3361

Ответ 1

Я думаю, все зависит от реальных требований к этому приложению. Если нет необходимости выполнять настраиваемую обработку ошибок - не делайте этого. Если никакая программа не должна проверять код выхода вашего приложения - нет необходимости возвращать его; и я считаю, что есть случаи, когда эти требования не применяются.

Иными словами, минимальное минимально; сделать простейшую вещь, которая могла бы работать. И если приложение yor удовлетворяет требованиям, я думаю, мы можем назвать это хорошо.

Ответ 2

Мне хотелось бы, чтобы заменить 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(), чтобы знать, что он создан через консольное приложение.

Ответ 3

По этому вопросу я нашел эту статью наиболее глубокой и информативной:

Консольные приложения в .NET или обучение новым трюкам новой собаки

Майкл Брук

Журнал MSDN февраль 2004 г.

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

Ответ 4

Я использую emacs, а вещь называется defaultcontent.el. Если вы не знакомы, Emacs является текстовым редактором и очень расширяем. defaultcontent.el - это расширение, которое вставляет (неожиданно) содержимое по умолчанию в новые файлы, когда они сначала создаются emacs.

Итак, если я попытаюсь открыть файл .cs, который не существует, emacs создает его, а затем помещает в него кучу содержимого по умолчанию. Я решаю, какой контент включать в новые файлы. Для моих файлов С# этот контент включает в себя:

  • простая документация с меткой времени, авторским правом и другими шаблонами.
  • связка атрибутов сборочной области (FileVersion, AssemblyDescription и т.д.)
  • один класс
  • contructor, который принимает массив строковых аргументов и анализирует их; логика разбора является оператором switch, с некоторым шаблоном для синтаксического анализа целых чисел, строк и булевых элементов.
  • a Способ использования, который печатает информацию об использовании.
  • Основной метод, который создает экземпляр класса и вызывает "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();
      }
    }

  }
}

У меня также есть функция defaultcontent для .c,.cpp,.vb,.csproj,.xml,.xsd,.wsdl, makefile и многих других типов файлов.