2010-04-22 8 views
10

Recibo una advertencia cuando ejecuto algún código a través de la utilidad de análisis de código de Visual Studio que no estoy seguro de cómo resolver. Quizás alguien aquí haya encontrado un problema similar, lo haya resuelto y esté dispuesto a compartir sus ideas.Referencia de objeto de paso CA2000 al constructor base en C#

Estoy programando una celda personalizada pintada que se usa en un control DataGridView. El código se parece a:

public class DataGridViewMyCustomColumn : DataGridViewColumn 
{ 
    public DataGridViewMyCustomColumn() : base(new DataGridViewMyCustomCell()) 
    { 
    } 

Se genera la advertencia siguiente:

CA2000: Microsoft.Reliability: En el método 'DataGridViewMyCustomColumn.DataGridViewMyCustomColumn()' llamar System.IDisposable.Dispose en el objeto 'nuevo DataGridViewMyCustomCell() 'antes de que todas las referencias estén fuera del alcance.

entiendo que me está advirtiendo DataGridViewMyCustomCell (o una clase que hereda de) implementa la interfaz IDisposable y el método Dispose() debe ser llamado a limpiar cualquier recurso reclamadas por DataGridViewMyCustomCell cuando ya no lo es.

Los ejemplos que he visto en Internet sugieren un bloque de uso para determinar la vida útil del objeto y hacer que el sistema lo elimine automáticamente, pero la base no se reconoce cuando se mueve al cuerpo del constructor para que pueda ' Escribo un bloque de uso a su alrededor ... que no estoy seguro de querer hacer de todos modos, ya que ¿no sería eso lo que indicaría el tiempo de ejecución para liberar el objeto que aún podría usarse más adelante dentro de la clase base?

Mi pregunta es, ¿el código está correcto como está? O bien, ¿cómo podría refactorizarse para resolver la advertencia? No quiero suprimir la advertencia a menos que sea realmente apropiado hacerlo.

Respuesta

18

Si está utilizando Visual Studio 2010, entonces CA2000 está completamente roto. También se puede romper en otras versiones de FxCop (a.k.a. Code Analysis), pero VS2010 es el único que puedo responder. Nuestro código base está dando advertencias CA2000 de código como este ...

internal static class ConnectionManager 
{ 
    public static SqlConnection CreateConnection() 
    { 
     return new SqlConnection("our connection string"); 
    } 
} 

... lo que indica que no está dispuesta la conexión antes de que se sale del ámbito en el método. Bueno, sí, eso es cierto, pero no está fuera del alcance para la aplicación, ya que se devuelve a una persona que llama: ese es el objetivo del método. De la misma manera, su argumento de constructor no está fuera del alcance, sino que se está pasando a la clase base, por lo que es un falso positivo de la regla en lugar de un problema real.

Esto solía ser una regla útil, pero ahora todo lo que realmente puede hacer es apagarlo hasta que lo arreglen. Lo cual es desafortunado, porque los (muy pocos) positivos reales son cosas que deberían arreglarse.

+3

+1 para 'completamente roto en VS10'. Es muy difícil convencer a la gente para que se una a mí en un paseo de mejora de la calidad del código cuando las herramientas me hacen parecer tan estúpido ... –

0

Gracias, Greg. Esa es mi preocupación, los pocos aspectos positivos que debería se han solucionado. Bueno, quizás en la próxima iteración de VS será más preciso. Voy a suprimir la advertencia en el código por el momento.

+0

Puse la siguiente supresión en mi código: '[System.Diagnostics.CodeAnalysis.SuppressMessage (" Microsoft .Reliability "," CA2000: Eliminar objetos antes de perder el alcance. Esta es una regla incorrecta. Consulte http://stackoverflow.com/q/2687398/228059 para obtener más información ")]' – noonand

+0

O puede crear un archivo .ruleset, seleccionar qué reglas quiere conservar y asociarlas a Build Types en las propiedades de la solución. Aquí está [un ejemplo] (https://github.com/madskristensen/WebEssentials2013/blob/master/EditorExtensions/CodeAnalysis.ruleset). Por defecto, VS abrirá un editor de modo de diseño con casillas de verificación para el archivo .ruleset. –

1

No hay manera segura y elegante para tener un constructor encadenado pasar una nueva IDisposable objeto al constructor base, ya que como se nota que no es posible envolver la llamada al constructor encadenado en cualquier tipo de try finally bloque.Hay un enfoque que es seguro, pero es apenas elegante: definir un método de utilidad algo como:

internal static TV storeAndReturn<TR,TV>(ref TR dest, TV value) where TV:TR 
{ 
    dest = value; return value; 
} 

tienen el aspecto constructor de algo como:

Código
protected DataGridViewMyCustomColumn(ref IDisposable cleaner) : 
    base(storeAndReturn(ref cleaner, new DataGridViewMyCustomCell())) 
{ 
} 

que necesita un nuevo objeto tendría entonces llamar a un método de fábrica estático público que llamaría al constructor apropiado dentro de un bloque try/finally cuya línea principal anularía cleaner justo antes de que terminara, y cuyo bloque finally llamaría Dispose en cleaner si no es nulo Siempre que cada subclase defina un método de fábrica similar, este enfoque asegurará que el nuevo objeto IDisposable será eliminado incluso si ocurre una excepción entre el momento en que se creó y el momento en que el objeto encapsulante se expone al código del cliente. El patrón es feo, pero no estoy seguro de que otro patrón más agradable asegure la corrección.

Cuestiones relacionadas