2012-06-28 6 views
14
entsorgen

der Regel die folgenden tun könnten Sie, wenn Sie ein eigenes Mitglied verfügen:Sagen FxCop ruft eine andere Methode

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

Der Zweck der lokalen Zuordnung ist eine Race-Bedingung zu vermeiden, wenn ein anderer Thread den privaten zuweisen könnte Mitglied nach dem Null-Check null sein. In diesem Fall ist es mir egal, ob Dispose zweimal für die Instanz aufgerufen wird.

Ich benutze dieses Muster die ganze Zeit so das ich schrieb eine Erweiterungsmethode tun:

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(); 
    } 
} 

Und jetzt in meiner Klasse, kann ich dies nur tun:

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

Problem ist, FxCop hat keine Ahnung, dass ich das mache und es gibt mir die CA2000: Dispose objects before losing scope Warnung in jedem Fall.

Ich mag diese Regel nicht auszuschalten, und ich möchte nicht jeden Fall zu unterdrücken. Gibt es eine Möglichkeit zu FxCop anzudeuten, dass diese Methode zu Dispose entspricht, soweit sie betroffen ist?

+3

Sie Probe sagt 'this.privateMember.Dispose()'. Sagt Ihr Code tatsächlich 'this.privateMember.SafeDispose()'? –

Antwort

8

Die kurze Antwort lautet: gibt es keine Möglichkeit, dass das Objekt anzudeuten, wird an anderer Stelle angeordnet ist.

Ein wenig Reflector-ing (oder dotPeek-ing, oder was auch immer) erklärt, warum.

FxCop ist in C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop. (Passen Sie für Ihr OS/VS-Versionskombination entsprechend an.) Regeln befinden sich in dem Rules-Unterverzeichnis.

Im Haupt FxCop Ordner, offen

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

Im Rules Ordner, offen DataflowRules.dll.

In DataflowRules.dll finden Phoenix.CodeAnalysis.DataflowRules.DisposeObjectsBeforeLosingScope. Das ist die eigentliche Klasse, die die Auswertung durchführt.

Mit Blick auf den Code dort, können Sie zwei Dinge von Interesse in Bezug auf Ihre Frage sehen.

  1. Es nutzt ein gemeinsamer Dienst namens SharedNeedsDisposedAnalysis.
  2. Es stammt von FunctionBodyRule.

Der erste Punkt ist interessant, weil SharedNeedsDisposedAnalysis was bestimmt, welche Symbole müssen Dispose() genannt. Es ist ziemlich gründlich, einen "Spaziergang" durch den Code zu machen, um festzustellen, was entsorgt werden muss und was tatsächlich entsorgt wird. Es hält dann eine Tabelle dieser Dinge für den späteren Gebrauch. Der zweite Artikel ist interessant, weil FunctionBodyRule Regeln den Körper einer einzelnen Funktion bewerten. Es gibt andere Regeltypen wie FunctionCallRule, die Dinge wie Funktionsaufruf-Mitglieder (z. B. ProvideCorrectArgumentsToFormattingMethods) auswerten.

Der Punkt ist, zwischen dem Potential „miss“ in diesem SharedNeedsDisposedAnalysis Dienst, wo es möglicherweise nicht durch Ihre Methode wird Rekursion, um zu sehen, dass die Dinge tatsächlich angeordnet sind immer und die Begrenzung von FunctionBodyRule nicht über die Funktion Körper geht, es ist nur Ihre Erweiterung nicht abfangen. Diese

ist der gleiche Grund, „Guard-Funktionen“ wie nie als Validierung des Argument gesehen, bevor Sie es benutzen - FxCop werden Sie immer noch sagen, das Argument für null zu überprüfen, auch wenn das, was die „Schutzfunktion“ tut .

Sie haben grundsätzlich zwei Möglichkeiten.

  1. Probleme ausschließen oder die Regel deaktivieren. Es wird auf keinen Fall machen, was Sie wollen.
  2. Erstellen Sie eine benutzerdefinierte/abgeleitete Regel, die Erweiterungsmethoden versteht. Verwenden Sie Ihre benutzerdefinierte Regel anstelle der Standardregel.

Nach geschrieben Brauch mit FxCop selbst regiert, werde ich Sie wissen lassen, fand ich es ... nicht-triviale. Wenn Sie diesen Weg gehen, während die Empfehlung in der Welt ist, den neuen Phoenix-Engine-Regelstil zu verwenden (das ist, was der aktuelle DisposeObjectsBeforeLosingScope verwendet), fand ich es einfacher, die älteren/Standard-FxCop SDK-Regeln zu verstehen (siehe FxCopSdk.dll in der Hauptordner von FxCop). Reflektor wird eine große Hilfe sein, herauszufinden, wie man das macht, da es so gut wie keine Dokumentation gibt. Suchen Sie in den anderen Assemblys im Ordner Rules nach Beispielen.

+1

Tatsächlich gibt es einen Mechanismus für die Erkennung von Schutzfunktionen durch CA1062: Dekoration mit einem Attribut namens ValidatedNotNullAttribute. Leider gibt es keinen ähnlichen Mechanismus für die Dispositionsregeln. –

+0

Danke Travis! Kennen Sie eine gute Anleitung zum Schreiben von benutzerdefinierten Regeln? Ich habe fxcop als Teil meines Builds ausgeführt und ich möchte sicherstellen, dass es die Regeln von der Quelle (nicht von den Programmdateien) laden kann. – Haacked

+0

Ich fand [diese] (http://www.binarycoder.net/fxcop/pdf/fxcop.pdf) Whitepaper sehr nützlich. – riezebosch

1

ich nicht bin, bedeutet einen FxCop Experten überhaupt, aber tut this question über die Verwendung von SuppressMessage sie beantworten? Ich weiß nicht, ob FCDCOP diese Nachricht bei der Analyse der Methoden, die es aufrufen, unterdrücken würde, aber es scheint, als ob es einen Versuch wert ist, wenn Sie Ihre SafeDispose-Methode mit dem SuppressMessage-Attribut dekorieren.

Sie die Syntax unten nicht vertrauen, aber so etwas wie:

[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

Diese Codeanalyse-Regel ist aus allen Gründen, die Travis umrissen hat, problematisch. Es scheint einen "neuen" Vorgang anzuhalten, und wenn der Entsorgungsaufruf nicht beendet ist, wird CA2000 ausgelöst.

Statt neu zu verwenden, rufen Sie eine Methode mit diesem im Körper:

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; 

Was das bedeutet ist, jede Möglichkeit der Initialisierung entfernen Sie das Objekt verursacht für die Entsorgung nicht erreichbar sein. In Ihrem Fall, wenn Sie ein privates Mitglied neu erstellen, würden Sie es innerhalb einer Methode tun, die wie die obige Methode aussieht. Nach der Verwendung dieses Musters sind Sie natürlich immer noch für die korrekte Entsorgung verantwortlich, und Ihre Erweiterungsmethode ist eine großartige Möglichkeit, diesen Null-Check zu verallgemeinern.

Ich habe festgestellt, dass Sie CA2000 vermeiden können, während IDisposables immer noch herumgereicht wird und Sie mit ihnen machen, was Sie wollen - solange Sie sie innerhalb einer Methode wie dem oben genannten neu erstellen. Probieren Sie es aus und lassen Sie es mich wissen, wenn es für Sie funktioniert. Viel Glück und gute Frage!

Andere Fixes für diese Regel (einschließlich dieser) sind hier aufgeführt: CA2000: Dispose objects before losing scope (Microsoft)

Verwandte Themen