2010-01-20 14 views
13

Me acaban de refactorizado código de un colega que, más o menos, se veía así ...¿Es costoso crear objetos en .Net?

public class Utility 
    public void AddHistoryEntry(int userID, HistoryType Historytype, int companyID) 
    { 
    // Do something... 
    } 
    public void AddHistoryEntry(int userID, HistoryType historyType, int companyID, string notes) 
    { 
    // Do something... 
    } 
} 

Para esto ...

public class HistoryEntry 
{ 
    public long UserID { get; private set; } 
    public HistoryType HistoryType { get; private set; } 
    public long CompanyID { get; set; } 
    public string Notes { get; set; } 

    public HistoryEntry(long userID, HistoryType historyType) 
    { 
    this.UserID = userID; 
    this.HistoryType = historyType; 
    } 
} 

public class Utility 
{ 
    public void AddHistoryEntry(HistoryEntry entry) 
    { 
    // Do something... 
    } 
} 

}

Ahora, esto es mucho mejor código diseño y es un favorito del tío Bob. Sin embargo, mi colega argumenta que es mucho más caro generar un objeto cada vez que queremos llamar a este método.

¿Está correcto?

una explicación más detallada

Gracias por todas las respuestas hasta el momento. Dejé fuera muchos detalles con la esperanza de la brevedad, pero algunos de ellos han causado problemas con las respuestas.

  • La clase de utilidad no existe. Solo quería poner los métodos en una clase para que todos puedan ver. En realidad, está en una clase sensata.
  • Hubo, de hecho, 5 métodos AddHistoryEntry en el código original. Todo lo cual tomó muchos parámetros int. Una razón para la refactorización fue que AddHistory(0, 1, 45, 3); realmente no dice mucho.
  • AddHistoryEntry no se llama desde un bucle cerrado, pero se usa ampliamente en toda la aplicación.

correcciones

ahora he actualizado los ejemplos de código, ya que había cometido un error con algunos de los parámetros.

+12

A menos que el código se vaya a ejecutar en una tostadora, siempre elegiría la versión más fácil de leer y fácil de mantener. La Optimización prematura es la raíz de todo mal: http://c2.com/cgi/wiki?PrematureOptimization –

+1

La palabra clave ** this ** casi nunca se requiere en C#. Si le preocupa el código limpio, elimínelo. Esto no es Java. –

+2

@DavidLively: acepto y nunca lo uso en mi código. Sin embargo, el motor de marcado Stack Overflow lo quería. –

Respuesta

19

Puede ser correcto si tiene millones de estos objetos en la memoria al mismo tiempo. Pero si no lo hace, entonces él está planteando lo que es casi seguro un punto discutible. Siempre elija primero un mejor diseño y luego modifíquelo solo si no cumple con los requisitos de rendimiento.

+0

19 votos? Guau. Estoy impresionado. –

5

¿Por qué no un método estático en la clase en sí?

public static void AddHistoryEntry(HistoryEntry entry)

le ahorra la clase de utilidad. El nuevo objeto puede ser costoso, puede que no, pero probablemente no importe. Los buenos valores predeterminados le permiten extender su código mucho más fácilmente, y ahorrará más tiempo en mantenimiento que en rendimiento, a menos que lo haga de forma continua.

+0

AddHistoryEntry debería ser realmente un miembro de ALGUNOS objetos útiles, ¿no? No funciona en el vacío. Pero si necesitas absolutamente una clase de utilidad estática, está bien ... bien. ¿Sería mejor AddHistoryEntry como parte de una clase como History ... por lo que podría decir myHistory.addHistoryEntry (entrada HistoryEntry)? –

+0

Realmente no podemos suponer que es posible convertirlo en un método estático cuando no sabemos nada sobre lo que realmente hace el método. Los métodos estáticos con efectos secundarios generalmente también son una mala idea. Lo siento, pero eso es un -1. – Aaronaught

+0

He respondido a esto en mi pregunta principal. –

1

¿Es esto realmente un cuello de botella de rendimiento? Si no, entonces sería un caso claro de optimización prematura para evitar el uso de objetos en este caso. La capacidad de mantenimiento y el diseño deben tener prioridad sobre el rendimiento hasta que comiences a abordar problemas de rendimiento significativos.

+0

Creo que @Chris Arnold era simplemente curioso. –

15

Crear un nuevo objeto es muy barato. Tendría que crear muchos objetos en un ciclo muy cerrado para notar cualquier diferencia ... pero al mismo tiempo, no es gratis. También debe tener en cuenta que los costos están medio ocultos: tiene algún costo inicial cuando crea el objeto, pero también significa que el recolector de basura tiene más trabajo por hacer.

Entonces, ¿un diseño más limpio vale la pena el rendimiento?No podemos decirte eso, depende de cómo se use tu código. Sospecho fuertemente que el rendimiento alcanzado será insignificante, pero si es usando esto en un circuito cerrado, puede que no lo sea. Sin embargo, hay una sola forma de averiguarlo: mídelo si está preocupado. Personalmente, probablemente solo buscaría el diseño más limpio y solo verificaría el rendimiento cuando parecía que se estaba convirtiendo en un problema.

(Si este método está golpeando disco o una base de datos, por cierto, la diferencia es casi obligado a ser insignificante.)

Una sugerencia, por cierto: ¿su tipo HistoryEntry realidad necesita ser ¿mudable? ¿Podría hacer que todas las propiedades sean de solo lectura, respaldadas por variables privadas de solo lectura?

Solo por mencionar el punto de Will - De alguna manera estoy de acuerdo con él. Si estos dos métodos son los solo lugares en los que necesita este concepto, no estoy del todo seguro de que valga la pena crear un nuevo tipo por el mero hecho de hacerlo. No por el aspecto de rendimiento, sino simplemente porque está presentando un grupo extra de código sin ningún beneficio demostrado. Aquí se demuestra la palabra clave: si en realidad está usando la misma colección de parámetros en otros lugares, o podría poner la lógica en la clase HistoryEntry, eso es completamente diferente.

EDIT: Sólo para responder al punto sobre varios argumentos enteros - C# 4 permitirá argumentos con nombre que debería hacer esto más fácil. Por lo que se podría llamar:

AddHistoryEntry(userId: 1, companyId: 10); 

Hacer los nombres visible con una clase adicional necesita uno de:

  • argumentos de constructor con nombre de C# 4 (en cuyo caso no eres mejor que con el método)
  • tipos mutables (urgh - que podría conducir a la difícil de rastrear errores que no habrían sido en la versión original)
  • largos nombres de los métodos estáticos para construir casos ("FromUserAndCompanyId") que se convierten en difíciles de manejar con más parámetros
  • Un constructor mutable tipo
  • Un montón de métodos "con": new HistoryEntry().WithCompanyId(...).WithUserId(...) - esto hace que sea más difícil validar en tiempo de compilación que ha proporcionado todos los valores requeridos.
+0

+1 por mencionar refactorización con muy poco beneficio DRY (sin perjuicio conceptual). –

+0

Hmmm, no estoy tan seguro de las observaciones de refactorización (¡hablando contigo también, Will!).La técnica de refactorización más beneficiosa (bueno, * potencialmente * beneficiosa y I * podría * exagerar un poco) no tiene ningún beneficio DRY: http://www.refactoring.com/catalog/renameMethod.html. –

+0

@Jeff: Tenga en cuenta que en realidad no mencioné DRY - Acabo de decir que no hubo beneficios demostrados, y sigo creyendo que ese es el caso. El término "HistoryEntry" ya está presente en la primera forma (en el nombre del método) por lo que no agrega ningún contexto allí ... –

4

Ambos están mal, pero él es más malo que tú.

+3

Está bien, lo veo venir, así que seré más claro. Estás refactorizando (parece) por refactorear y está optimizando prematuramente. – Will

+0

+1, aunque es mucho más para el comentario que la respuesta real. (Sugiero que edites.) He tocado esto en una edición de mi respuesta, también. –

+3

@jon Lo sé, es más apropiado en la respuesta. Sin embargo, el artista en mí se niega a destruir una respuesta hermosa (ojo, espectador) por razones prácticas. Se puede leer de muchas maneras y, por lo tanto, estimula la mente para que piense en las implicaciones del camino que toma a través del código de otra persona. O algo. – Will

4

Es costoso en relación con no crear objetos en .NET, de la misma manera que 1 es un número grande relativo a 0. Todo depende de su perspectiva. Si esto está en un bucle que se está ejecutando 20 millones de veces, podría preocuparme; de lo contrario, no me importaría.

Una vez dicho esto, me gustaría señalar que Utility no es un buen nombre para una clase, y refactorización en un HistoryEntry también es probable que se requiere para verificar que el parámetro que se pasó no se null, un requisito que no tenías originalmente cuando todos los parámetros eran tipos de valores.

también es comúnmente considerado un anti-patrón de tener objetos con datos, pero ningún comportamiento, que parece ser HistoryEntry. Intenta evitar hacer esto con demasiada frecuencia.

+2

+1 para 'Utility'. Simplemente mezcle todo en una sola clase llamada "Cosas" whydontcha? – Will

+1

A menos que esté creando una lista de historial de acciones del cliente para sus incidentes de centro de llamadas de Pacific Gas & Electric. –

0

Es un poco más costoso desde el punto de vista computacional, pero por ese costo obtiene el beneficio de un código más claro y conceptual que puede responder más rápidamente a las demandas comerciales futuras. Rendimiento vs agilidad.

2

No es realmente costoso crear objetos en .NET. De hecho, la creación de objetos es muy rápida, incluso si creas millones de ellos.

Sin embargo, la recolección de basura es no a buen precio. Si creas muchos objetos efímeros, es posible que experimentes una desaceleración considerable cuando se active la función gc. Tiene sentido evitar crear nuevos objetos efímeros si la creación se ejecuta miles o millones de veces. Si, por ejemplo, crea varios objetos con cada paquete de red recibido por un servidor con miles de conexiones.

Dicho esto, nunca es aconsejable evitar llamar new antes que demuestra que una gran cantidad de objetos realmente hacen frenar GC. En tu ejemplo, probablemente no lo harían.

0

No. El recolector de basura está optimizado para ser altamente eficiente cuando se trata de objetos efímeros.

Cuestiones relacionadas