2009-10-27 20 views
23

¿Cuál sería el mínimo código repetitivo para una aplicación de consola de punto de entrada de C# que haría un buen comportamiento ciudadano?mínimo, buen ciudadano, C# aplicación de consola repetitivo

Cuando alguien va a crear un proyecto aplicación de consola utilizando Visual Studio (hasta el año 2008 en el momento de la escritura), se le presentará con un texto modelo Program.cs que tiene este aspecto:

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

Hay son, sin embargo, algunas cosas que todo el mundo tiene que hacer para que una aplicación de consola sea mínimamente buen ciudadano. Por ejemplo, si se produce una excepción, escriba un mensaje limpio en el error estándar (Console.Error) y no en la salida estándar (Console.Out). Del mismo modo, establezca el código de error en un valor distinto de cero en el caso de algún error para que los procesos de llamada puedan detectar fallas.

¿Cuál sería el mínimo código repetitivo para una aplicación de consola de punto de entrada de C# que haría un buen comportamiento ciudadano? ¿Qué agregarías o cambiarías a lo siguiente?

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; 
     } 
    } 
} 

Lo que este texto modelo logra:

  • Si se produce una excepción:
    • un mensaje limpio se visualiza como error estándar
    • los detalles completos se trazan
    • la el código de salida se establece en un valor arbitrario distinto de cero (100) a menos que ya esté configurado
  • rastreo se puede activar de forma dinámica a través de un interruptor detallado
  • rastreo se envía al error estándar para no interferir con la salida verdadera
  • Al finalizar con éxito, el código de salida refleja el último valor Environment.ExitCode , que es por lo general cero, pero se puede cambiar por código aguas abajo
  • Program clase es estática

no-objetivos de esta pregunta:

  • Identificar argumentos de línea de comandos código de manejo o biblioteca
  • identificar un registro externo o trazado biblioteca
+1

Una es la cuestión? :) En serio, deberías haberlo metido un poco en una pregunta. Y tal vez una respuesta. –

+3

La pregunta está en negrita. –

+0

En negrita, sí. Y 20 líneas abajo. –

Respuesta

3

A mí me gustaría ver Run(args) sustituye por el instanciación de una clase.Algo así como:

Try principal:

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; 
    } 
} 

algo como esto va a desalentar el código de procedimiento y fomentar la creación de un enfoque orientado a objetos.

En lo que respecta a la AClass() constructor, creo que los argumentos deben ser procesados ​​antes de ser pasado a AClass()AClass() en lugar de tener que saber que es creado a través de una aplicación de consola.

7

Creo que todo depende de los requisitos reales para esa aplicación. Si no es necesario realizar un manejo de errores personalizado, no lo haga. Si ningún programa necesita verificar el código de salida de su aplicación, no es necesario devolverlo; y creo que hay casos en que esos requisitos no se aplican.

En otras palabras, lo mínimo es mínimo; haz lo más simple que pueda funcionar. Y si su aplicación cumple con los requisitos, creo que podemos llamarla "buena conducta".

+1

+1 para recomendar contra la sobreinnovación de la robustez. – dsimcha

0

Yo uso emacs, y una cosa llamada defaultcontent.el. Si no está familiarizado, Emacs es un editor de texto, y es muy extensible. defaultcontent.el es una extensión que inserta (sorprende) contenido predeterminado en archivos nuevos, cuando los crea primero emacs.

Así que si intento abrir un archivo .cs que no existe, emacs lo crea y luego le agrega un montón de contenido predeterminado. Decido qué contenido incluir en nuevos archivos. Para mis archivos C#, este contenido incluye:

  • documentación simple con la marca de tiempo, derechos de autor, otra repetición.
  • un manojo de montaje alcance atributos (FileVersion, AssemblyDescription, etc)
  • una sola clase
  • un contructor que acepta una matriz de argumentos de cadena, y los analiza; la lógica de análisis es una instrucción de conmutación, con algunas repeticiones para analizar enteros, cadenas y booleanos.
  • a Método de uso, que imprime la información de uso.
  • a Método principal, que ejemplifica la clase y llama a "Ejecutar()"; esto está rodeado de una try..catch y en la captura, se llama al método Usage(). Si se produce algún error en algún lugar del programa, se muestra el mensaje de uso.

El contenido predeterminado también me permite colocar el cursor donde quiero. En mi caso, eso está en el medio del método Run() vacío.

Esta es mi contenido por defecto:

// 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(); 
     } 
    } 

    } 
} 

También he defaultcontent establecido para .c, .cpp, .vb, .csproj, .xml, .xsd, .wsdl, makefile, y muchos otros tipos de archivo.

Cuestiones relacionadas