2009-12-11 9 views
9
public void DoSomething(params object[] args) 
{ 
    // ... 
} 

El problema con la firma anterior es que cada tipo de valor que se pasará a ese método estará enmarcado implícitamente, y este es un serio problema de rendimiento para mí.¿Número de variable de argumentos sin boxeo de los tipos de valor?

¿Hay alguna manera de decidir un método que acepte un número variable de argumentos sin incluir los tipos de valor en el cuadro de texto?

Gracias.

Respuesta

12

Puede utilizar los genéricos:

public void DoSomething<T>(params T[] args) 
{ 
} 

Sin embargo, esto sólo permitirá a un solo tipo de ValueType que se determine. Si necesita mezclar o combinar tipos de valores, tendrá que permitir que ocurra el boxeo, como lo está haciendo ahora, o proporcionar sobrecargas específicas para diferentes números de parámetros.


Editar: Si necesita más de un tipo de parámetro, puede usar sobrecargas para lograr esto, hasta cierto punto.

public void DoSomething<T,U>(T arg1, params U[] args) {} 
public void DoSomething<T,U>(T arg1, T arg2, params U[] args) {} 

Desafortunadamente, esto requiere múltiples sobrecargas para sus tipos.

Como alternativa, puede pasar directamente matrices:

public void DoSomething<T,U>(T[] args1, U[] args2) {} 

Se pierde la sintaxis compilador agradable, pero entonces puede tener cualquier número de dos parámetros pasados.

+0

sí, pero esto limita el tipo de toda la lista de argumentos a un solo tipo ... – DxCK

+0

Puede mezclar y combinar con sobrecargas ... –

+0

Lo he editado para agregar más información aquí ... –

3

Actualmente, no, y no he visto nada que aborde el problema en la información de .NET 4 que se ha publicado.

Si se trata de un gran problema de rendimiento para usted, puede considerar varias sobrecargas de listas de parámetros comúnmente vistos.

Me pregunto, sin embargo: ¿es realmente un problema de rendimiento, o está optimizando prematuramente?

-1

En C# 4.0 usted puede utilizar parámetros nombrados (y por lo tanto, opcionales)! Más información en this blog post

+1

OMG ... Es un tema completamente diferente. –

+0

Hmmm ...Todavía no estoy seguro de cuál es la funcionalidad requerida, así que tal vez estoy equivocado y tal vez no. La frase "acepta una cantidad variable de argumentos sin encajonar los tipos de valor", aunque creo que puede satisfacerse mediante parámetros nombrados ... –

3

Supongamos que el código que está llamando a este método conoce los tipos de argumentos. Si es así, puede empaquetarlos en el tipo apropiado Tuple de .NET 4, y pasar su instancia (Tuple es tipo de referencia) a dicho método como objeto (ya que no existe una base común para todas las Tuplas).

El principal problema aquí es que no es fácil procesar los argumentos dentro de este método sin boxing/unboxing, y probablemente, incluso sin reflexión. Trate de pensar qué se debe hacer para extraer, digamos, enésimo argumento sin boxeo. Al final comprenderá que debe tratar las búsquedas de diccionario allí (que impliquen Dictionary<K,V> o internal dictionaries utilizados por CLR) o con boxeo. Obviamente, las búsquedas de diccionario son mucho más costosas.

Estoy escribiendo esto porque en realidad desarrollamos una solución para un problema muy similar: debemos poder operar con our own Tuples sin boxeo, principalmente para comparar y deserializar (los tuplos son utilizados por el motor de base de datos que desarrollamos, por lo que de cualquier operación básica es realmente esencial en nuestro caso).

Pero:

  • terminamos con una solución bastante compleja. Eche un vistazo, por ej. al TupleComparer.
  • El efecto de la ausencia de boxeo no es tan bueno como esperábamos: cada operación de boxeo/desempaquetado se reemplaza por una única indexación de matriz y pocas llamadas a métodos virtuales, el costo de ambas maneras es casi idéntico.

El único beneficio del enfoque que hemos desarrollado es que no "inundamos" a Gen0 con la basura, por lo que las colecciones Gen0 ocurren con mucha menos frecuencia. Como el costo de la colección Gen0 es proporcional al espacio asignado por los objetos "activos" y a su recuento, esto ofrece una ventaja notable, si otras asignaciones se entremezclan con (o simplemente suceden durante) la ejecución del algoritmo que tratamos de optimizar de esta manera.

Resultados: después de esta optimización nuestras pruebas sintéticas mostraron de 0% a 200-300% de aumento en el rendimiento; por otro lado, la simple prueba de rendimiento del motor de la base de datos ha mostrado una mejora mucho menos impresionante (alrededor del 5-10%). Se desperdició mucho tiempo en las capas superiores (también hay un ORM bastante complejo), pero ... Lo más probable es que eso sea lo que verás después de implementar cosas similares.

En resumen, le aconsejo que se centre en otra cosa. Si quedará completamente claro que este es un problema de rendimiento importante en su aplicación, y no existen otras buenas maneras de resolverlo, bueno, continúe ... De lo contrario, simplemente está alentando a su cliente o al suyo haciendo el trabajo prematuro optimización.

0

Para una implementación completamente genérica, la solución común es utilizar un patrón fluido. Algo como esto:

public class ClassThatDoes 
{ 
    public ClassThatDoes DoSomething<T>(T arg) where T : struct 
    { 
     // process 

     return this; 
    } 
} 

Ahora se llama:

classThatDoes.DoSomething(1).DoSomething(1m).DoSomething(DateTime.Now)//and so on 

Sin embargo que no funciona con las clases estáticas (métodos de extensión están bien ya que se puede volver this).

Su pregunta es básicamente la misma: Can I have a variable number of generic parameters? pregunta de otra manera.

o aceptar una serie de artículos con params palabra clave:

public ClassThatDoes DoSomething<T>(params T[] arg) where T : struct 
{ 
    // process 

    return this; 
} 

y llame:

classThatDoes.DoSomething(1, 2, 3) 
      .DoSomething(1m, 2m, 3m) 
      .DoSomething(DateTime.Now) //etc 

si la matriz creación de sobrecarga es menor que sobrecarga el boxeo es algo que tendrá que decidir usted mismo.

Cuestiones relacionadas