Минимальный, хороший гражданин, С# приложение приложения консоли
Каким будет минимальный код шаблона для входной точки приложения консоли С#, который сделает его хорошим человеком?
Когда кто-то выходит, чтобы создать проект Консольное приложение с помощью 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
класс статический
Без цели этого вопроса:
- Идентифицировать аргументы командной строки, обрабатывающие код или библиотеку
- Определите внешнюю библиотеку ведения журнала или трассировки
Ответы
Ответ 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 и многих других типов файлов.