2009-05-26 9 views
8

Recientemente he leído el siguiente mensaje de desbordamiento: Hidden Features of C#C# args de longitud variable, que es mejor y por qué: __arglist, params array o Dictionary <T,K>?

Una de las características señalado fue el arglist. ¿Por qué uno elegiría esta o las alternativas como un medio para usar una lista de argumentos de longitud variable para un método? Además, tenga en cuenta que probablemente no use este tipo de constructo en mi código a menos que un caso de esquina lo justifique. Esta es más una cuestión de semántica que si es incluso práctico o prudente usar incluso argumentos de longitud variable. Entonces, ¿alguien sabe cuál es mejor y por qué?

[Test] 
public void CanHandleVariableLengthArgs() 
{ 
    TakeVariableLengthArgs(__arglist(new StringBuilder(), 12)); 

    object[] arr = { new StringBuilder() }; 
    TakeVariableLengthArgs2(arr); 

    TakeVariableLengthArgs3(
     new Dictionary<string, object> 
     { { "key", new StringBuilder() } }); 
} 

public void TakeVariableLengthArgs(__arglist) 
{ 
     var args = new ArgIterator(__arglist); 

     var a = (StringBuilder)TypedReference.ToObject(args.GetNextArg()); 
     a.Append(1); 
} 

public void TakeVariableLengthArgs2(params object[] args) 
{ 
     var a = (StringBuilder)args[0]; 
     a.Append(1); 
} 

public void TakeVariableLengthArgs3(Dictionary<string, object> args) 
{ 
     var a = (StringBuilder)args["StringBuilder"]; 
     a.Append(1); 
} 
+17

El único mediante el diseño propósito de __arglist es por lo que se puede hacer de manera segura llamadas de interoperabilidad hacia y desde bibliotecas escritas en C++ que utilizan argumentos var estilo C. No lo uses para nada más que eso. –

+0

Si desea [pinvoke] (http://www.pinvoke.net/default.aspx/user32/wsprintf.html) la [función wsprintf] (http://msdn.microsoft.com/en-us/library /windows/desktop/ms647550(v=vs.85).aspx) tienes que usar '__arglist'. – Bitterblue

Respuesta

8

sin duda lo nunca utilice __arglist, ya que es indocumentado y nadie sabe lo que significa en cualquier caso.

También evitaría las listas de argumentos de longitud variable durante el mayor tiempo posible, y en su lugar volver a trabajar mi diseño para comprender qué es realmente variable y modelar esa variabilidad de una manera menos dependiente de la plataforma.

+0

Gracias por la entrada. No sabía que no estaba documentado. Aprecio que lo hayas señalado. Sin duda, una buena razón para desconfiar. ¿Conoce las diferencias de estado/comportamiento entre los 3 tipos (__arglist, params [] o Dictionary )? – mkelley33

+2

Uno es nunca ser utilizado, uno es una matriz, uno es un diccionario. Todos son a menudo un signo de diseño pobre. –

+2

Jajaja, está bien, así que veo dónde estás en eso. Tenía curiosidad sobre cuándo uno sería preferido por sobre el otro. Borro completamente sus tipos. Estoy buscando una explicación un poco más en profundidad. Aprecio tu crítica sobre el uso de cualquiera de ellos. Estoy de acuerdo al 100%, pero a veces interactuamos con otros, participando en un diseño deficiente, apretando los dientes y esperando lo mejor. – mkelley33

0

Preferiría no utilizar ninguna de las tres técnicas descritas aquí. En su lugar, diseñaría un objeto de valor que tenga tipos fuertes siempre que sea posible, y posiblemente incluso tipos anulables. Si viene el impulso, también puede crear un objeto de valor tipado genérico.

Hay tanto olor a código en esta forma de codificación para mí. A longitud variable colección de objeto? No pasaría desapercibido en mi revisión del código.

Editar: Y si lo hizo pasar mi opinión código del parámetro sería con toda probabilidad será una instancia IEnumerable y ninguno de los tres elementos sugerencias. IEnumerable es lo más delgado que podría encapsular mis necesidades.

+0

Estoy totalmente de acuerdo con usted, pero esta es simplemente una pregunta relativa a los pros y los contras en el contexto restringido de los tres tipos. – mkelley33

+0

Ok, cedido con una edición :) – krosenvold

3

Depende del caso. He usado params en casos en los que tengo una cantidad variable de argumentos y esto aumenta significativamente la legibilidad del código de llamada.

Por ejemplo, tengo una clase que representa un documento TIFF y permite el acceso a una colección de páginas que pueden reordenarse e intercalarse con otros documentos TIFF. Dado que una de las tareas más comunes que desean nuestros clientes es la capacidad de combinar fácilmente múltiples documentos TIFF en una sola, también ofrecemos los siguientes dos métodos de utilidad:

public static void Combine(Stream output, params Stream[] sources) { /* ... */ } 
public static void Combine(Stream output, params string[] sourceFiles) { /* ... */ } 

que en el lenguaje hacen que el código de cliente se sienta muy agradable:

using (FileStream output = new FileStream(outputPath, FileMode.Create)) { 
    TiffDocument.Combine(output, tpsCoverSheetPath, mainDocumentPath, tpsTrailerPath); 
} 
+0

Gracias por el ejemplo. ¿Cómo es esto más beneficioso que usar __arglist, Dictionary o incluso IEnumerable? ¿Cómo se agrega a la legibilidad? Por ejemplo, ¿qué considerarías menos legible? ¿De qué depende tu elección? – mkelley33

+0

Sure - C# no es realmente bueno para hacer listas dinámicas sin un uso intensivo de nuevo (nuevo Foo [] {nuevo Foo (a), nuevo Foo (b)}). Params quita un nivel de nueva salida, elimina un poco de confusión sintáctica, y en este ejemplo se siente bien en todo el caso de uso. IEnumerable es mejor cuando la lista de cosas necesita ser construida y manipulada programáticamente primero (es decir, agregar, eliminar, ordenar). El diccionario <> es mejor para la paramaterización no homogénea, pero para mí es desagradable. __arglist está fuera de combate. – plinth

3

En general, probablemente sea mejor que evite las características no documentadas del idioma, por varias razones.

  • Ellos son más propensos a cambiar entonces las, características documentados establecidos
  • Pueden tener efectos secundarios y las consecuencias que no son evidentes en su uso
  • Otros desarrolladores no están familiarizados con ellos y tendrán más dificultades para mantener su código
  • herramientas de refactorización (como VS sí mismo o Resharper) son poco probable que sea capaz de reconocerlos
  • le quitan la claridad de la intención de su código
  • Hay alternativas lenguaje apoyado a la mayoría de ellos

En el caso específico de la __arglist, se puede lograr la misma capacidad con el lenguaje soportado params palabra clave, que le permite crear listas de argumentos de variable de tipo seguro para métodos en C#. Como práctica, sin embargo, sería cuidadoso al usarlo, ya que puede ofuscar tu código si se usa de manera apropiada: buenos casos de uso (como los de cadena.Formato() que acepta argumentos variables) son menos frecuentes de lo que crees.

4

C# 4 tendrá un mejor mecanismo para esto; named and optional arguments:

static void Main(string[] args) 
{ 
    // The method can be called in the normal way, by using positional arguments. 
    Console.WriteLine(CalculateBMI(123, 64)); 

    // Named arguments can be supplied for the parameters in either order. 
    Console.WriteLine(CalculateBMI(weight: 123, height: 64)); 
    Console.WriteLine(CalculateBMI(height: 64, weight: 123)); 

    // Positional arguments cannot follow named arguments. 
    // The following statement causes a compiler error. 
    //Console.WriteLine(CalculateBMI(weight: 123, 64)); 

    // Named arguments can follow positional arguments. 
    Console.WriteLine(CalculateBMI(123, height: 64)); 
} 

static int CalculateBMI(int weight, int height) 
{ 
    return (weight * 703)/(height * height); 
} 
Cuestiones relacionadas