2012-06-28 11 views
14

Normalmente cuando se deshaga de un miembro privado, puede hacer lo siguiente:Dile a FxCop otro método está llamando disponer

public void Dispose() { 
    var localInst = this.privateMember; 
    if (localInst != null) { 
     localInst.Dispose(); 
    } 
} 

El propósito de la asignación local es evitar una condición de carrera en otro hilo podría asignar el privado miembro a ser nulo después de la verificación nula. En este caso, no me importa si se llama al Dispose dos veces en la instancia.

que utilizan este patrón todo el tiempo así que escribí un método de extensión para hacer esto:

public static void SafeDispose(this IDisposable disposable) 
{ 
    if (disposable != null) 
    { 
     // We also know disposable cannot be null here, 
     // even if the original reference is null. 
     disposable.Dispose(); 
    } 
} 

Y ahora en mi clase, sólo puede hacer esto:

public void Dispose() { 
    this.privateMember.SafeDispose(); 
} 

El problema es que, FxCop no tiene idea de que estoy haciendo esto y me da la advertencia CA2000: Dispose objects before losing scope en todos los casos.

No quiero desactivar esta regla y no quiero suprimir todos los casos. ¿Hay alguna manera de insinuarle a FxCop que este método es equivalente a Dispose en lo que a él respecta?

+3

Se muestra dice 'this.privateMember.Dispose()'. ¿Su código realmente dice 'this.privateMember.SafeDispose()'? –

Respuesta

8

La respuesta corta es: no hay manera de dar a entender que el objeto está dispuesta en otro lugar.

Un poco de Reflector-ing (o dotPeek-ing, o lo que sea) explica por qué.

FxCop está en C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop. (Ajuste según corresponda para su versión combo OS/VS.) Las reglas están en el subdirectorio Rules.

En la carpeta principal FxCop, abierta

  • Microsoft.VisualStudio.CodeAnalysis.dll
  • Microsoft.VisualStudio.CodeAnalysis.Phoenix.dll
  • phx.dll

En la carpeta Rules, abierta DataflowRules.dll.

En DataflowRules.dll encuentra Phoenix.CodeAnalysis.DataflowRules.DisposeObjectsBeforeLosingScope. Esa es la clase real que hace la evaluación.

Mirando el código allí, puede ver dos cosas de interés con respecto a su pregunta.

  1. Utiliza un servicio compartido llamado SharedNeedsDisposedAnalysis.
  2. Es deriva de FunctionBodyRule.

El primer artículo es interesante porque SharedNeedsDisposedAnalysis es lo que determina qué símbolos deben Dispose() llamada. Es bastante completo, haciendo un "recorrido" por el código para determinar qué se debe desechar y qué se desecha realmente. Luego mantiene una tabla de esas cosas para su uso posterior.

El segundo elemento es interesante porque FunctionBodyRule reglas evalúan el cuerpo de una sola función. Existen otros tipos de reglas, como FunctionCallRule, que evalúan elementos como los miembros de llamada de función (por ejemplo, ProvideCorrectArgumentsToFormattingMethods).

El punto es, entre el potencial de "miss" en ese SharedNeedsDisposedAnalysis servicio en el que no puede ser recursiva a través de su método de ver que las cosas realmente están siendo eliminados y la limitación de FunctionBodyRule no va más allá del cuerpo de la función, es sólo no atrapando su extensión.

Esta es la misma razón "funciones de guardia" como nunca se consiguen considerados como validar el argumento antes de usarla - FxCop todavía le dirá para comprobar el argumento a favor de nula a pesar de que eso es lo que la "función de protección" está haciendo .

Tiene básicamente dos opciones.

  1. Excluir problemas o desactivar la regla. No hay forma de que haga lo que usted quiere.
  2. Crea una regla personalizada/derivada que comprenderá los métodos de extensión. Usa tu regla personalizada en lugar de la regla predeterminada.

Después de haber escrito personalizado FxCop gobierna a mí mismo, yo lo haré saber lo encontré ... no trivial. Si avanza por ese camino, aunque la recomendación en el mundo es utilizar el nuevo estilo de regla de motor de Phoenix (eso es lo que usa el actual DisposeObjectsBeforeLosingScope), me resultó más fácil entender las reglas FxCop SDK más antiguas/estándar (consulte FxCopSdk.dll en la carpeta principal de FxCop). El reflector será de gran ayuda para descubrir cómo hacerlo, ya que hay prácticamente cero documentos sobre él. Consulte los otros ensambles en la carpeta Rules para ver ejemplos de ellos.

+1

Acutally, hay un mecanismo para el reconocimiento de funciones de guarda por CA1062: decoración con un atributo llamado ValidatedNotNullAttribute. Desafortunadamente, no existe un mecanismo similar para las reglas de disposición. –

+0

Gracias Travis! ¿Conoces una buena guía para escribir reglas personalizadas? Tengo fxcop ejecutándose como parte de mi compilación y quiero asegurarme de que pueda cargar las reglas desde la fuente (no desde los archivos de programa). – Haacked

+0

Encontré [este] (http://www.binarycoder.net/fxcop/pdf/fxcop.pdf) libro blanco muy útil. – riezebosch

1

No soy en absoluto un experto de FxCop, pero ¿usa this question para usar SuppressMessage para responderlo? No sé si decorar su método SafeDispose con el atributo SuppressMessage haría que FxCop suprima ese mensaje en su análisis de los métodos que lo llaman, pero parece que vale la pena intentarlo.

no confían en la siguiente sintaxis, sino algo así como:

[SuppressMessage("Microsoft.Design", "CA2000:Dispose objects before losing scope", Justification = "We just log the exception and return an HTTP code")] 
public static void SafeDispose(this IDisposable disposable) 
0

Esta regla de análisis de código es problemática, por todos los motivos trazados por Travis. Parece poner en cola cualquier operación "nueva", y a menos que la llamada a deshacerse esté cerca, CA2000 se activa.

En lugar de utilizar nuevo, llamar a un método con este en el cuerpo:

MyDisposableClass result; 
MyDisposableClass temp = null; 
try 
{ 
    temp = new MyDisposableClass(); 
    //do any initialization here 
    result = temp; 
    temp = null; 
} 
finally 
{ 
    if (temp != null) temp.Dispose(); 
} 
return result; 

Lo que esto hace es eliminar cualquier posibilidad de que la inicialización haciendo que el objeto sea inalcanzable para su eliminación. En su caso, cuando "nuevo" miembro privado, lo haría dentro de un método que se parece al método anterior. Después de usar este patrón, por supuesto, usted sigue siendo responsable de la eliminación correcta, y su método de extensión es una excelente manera de generalizar esa verificación nula.

He descubierto que puede evitar CA2000 mientras sigue pasando IDisposables y haciendo lo que quiera con ellos, siempre que los actualice correctamente con un método como el anterior. Pruébalo y avísame si te funciona. Buena suerte y buena pregunta!

Otras correcciones para esta regla (incluido éste) se describen aquí: CA2000: Dispose objects before losing scope (Microsoft)

Cuestiones relacionadas