2012-01-19 11 views
31

mam jakiś kod, który wyzwoli CA1063 Code Analysis Ostrzeżenie:Analiza Kod CA1063 pożary, gdy wynikające z IDisposable i zapewniające realizację w klasie bazowej

CA1063: Microsoft.Design: IDisposable Usuń z listy interfejsów realizowanych przez "Funkcjonalność" i nadpisanie klasy bazowej Zamiast tego należy użyć implementacji.

Jednak nie jestem pewien, co muszę zrobić, aby naprawić to ostrzeżenie.

Krótko, mam interfejs IFunctionality, który pochodzi od IDisposable. Klasa Functionality implementuje IFunctionality, ale wywodzi się z klasy Reusable, aby móc ponownie użyć som-kodu. Klasa Reusable również pochodzi z IDisposable.

public class Reusable : IDisposable { 

    ~Reusable() { 
    Dispose(false); 
    } 

    public void Dispose() { 
    Dispose(true); 
    GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(Boolean disposing) { 
    // ... 
    } 

    public void DoSomething() { 
    // ... 
    } 

} 

public interface IFunctionality : IDisposable { 

    void DoSomething(); 

    void DoSomethingElse(); 

} 

public class Functionality : Reusable, IFunctionality { 

    public void DoSomethingElse() { 
    // ... 
    } 

#if WORK_AROUND_CA1063 
    // Removes CA1063 
    protected override void Dispose(Boolean disposing) { 
    base.Dispose(disposing); 
    } 
#endif 

} 

mogę pozbyć się ostrzeżenia nadrzędnymi Dispose na Functionality i wywołanie klasy podstawowej Dispose choć robi to nie powinno zmienić semantykę kodu.

Więc czy jest coś o IDisposable w tym kontekście przeoczyłem, czy jest to po prostu CA1063, który wypala dla tego konkretnego konstruktu?

Wiem, że mogę stłumić CA1063, ale zasada jest dość szeroka i nie chcę przegapić żadnych innych problemów związanych z wdrażaniem IDisposable zgłoszonych przez tę regułę.

+0

Stare pytanie, ale problemy z CA1063 są wielokrotnie pokonania przez znakowanie klasy jako 'sealed'. Jeśli klasa nie jest "zapieczętowana", myślę, że klasa musi "virual" lub/i że metoda 'Dispose()' musi być 'chroniona' – mortb

Odpowiedz

34

Jest to wynik fałszywie dodatni z powodu drobnego błędu samej reguły. Podczas próby ustalenia, czy klasa ponownie implementuje IDisposable (po ustaleniu, że istnieje implementacja klasy podstawowej, którą można przesłonić), sprawdza tylko, czy interfejsy klasy zawierają IDisposable. Niestety, lista interfejsów wyświetlana w metadanych zestawu zawiera listę "rozstrzelonych" interfejsów, w tym wszelkie interfejsy odziedziczone za pośrednictwem interfejsów, które klasa jawnie implementuje w oryginalnym kodzie C#.Oznacza to, że FxCop widzi deklarację, która wygląda jak poniżej na swojej klasie funkcjonalność:

public class Functionality : Reusable, IFunctionality, IDisposable 
{ 
    ... 
} 

Given to reprezentacja metadane, reguła ImplementIDisposableCorrectly powinien być nieco bardziej inteligentny, jak to stara się ustalić, czy klasa jest rzeczywiście ponowne wdrożenie IDisposable (na przykład, patrząc na jawną implementację Dispose(), jeśli klasa bazowa ma opcję overridable Dispose (bool)). Jednak, zważywszy, że reguła tego nie robi, najlepszym wyjściem jest stłumienie fałszywych alarmów.

BTW, polecam poważnie biorąc pod uwagę użycie SuppressMessageAttribute do tłumienia fałszywych alarmów zamiast aktualnej metody kompilacji warunkowej. np .:

[SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", 
    Justification = "False positive. IDisposable is inherited via IFunctionality. See http://stackoverflow.com/questions/8925925/code-analysis-ca1063-fires-when-deriving-from-idisposable-and-providing-implemen for details.")] 
public class Functionality : Reusable, IFunctionality 
{ 
    ... 
} 

Ponadto, warto poważnie rozważyć getting rid of the finalizer ...

+0

+1 za wskazówkę dotyczącą SupressMessage –

+0

Łącze do "pozbycia się finalizatora" uległo zgnieceniu. Nie mogę stwierdzić, czy pierwotnie wskazywał na http://joeduffyblog.com/2005/12/27/never-write-a-finalizer-again-well-almost-never/ lub http://joeduffyblog.com/2008/02/17/idisposable-finalization-and-concurrency /, lub może jakiś inny artykuł, którego jeszcze nie znalazłem. Czy mógłbyś edytować swoją odpowiedź, aby poprawić link? –

+0

Gotowe. (Twoje pierwsze przypuszczenie było słuszne.) –

1

Jest to związane z korzystaniem z IDisposable, a nie z samego interfejsu. Po prostu implementujesz zalecany wzorzec do jego używania, dostarczając i zastępując chronioną metodę Dispose(bool) - nie jest to część samego interfejsu.

Jeśli twoja metoda zastąpienia nie dodaje niczego, to nie ma problemu z pominięciem go. Ostrzeżenie CA1063 ma na celu uwydatnienie tego problemu, ale jeśli wiesz, że w rzeczywistości nie ma żadnych zasobów do rozwiązania, możesz go obejść, podając pustą implementację, lub wyłączając tę ​​konkretną regułę dla tego konkretnego przypadku. plik.

Możesz to zrobić za pomocą atrybutu [SuppressMessage].

4

Twoje "obejście" jest tutaj właściwym wzorcem dla klasy pochodnej, która ponownie implementuje IDisposable.

Ale myślę, że powinieneś ponownie rozważyć projekt IFunctionality : IDisposable. Czy bycie jednorazowym naprawdę jest problemem o numerze IFunctionality? Myślę, że ta decyzja należy do klasy wykonawczej.

+0

'IDisposable' jest ważną częścią umowy o' IFunctionality', która jest używany przez resztę systemu. Jednakże 'Reusable' zapewnia implementację' Dispose'. Stąd projekt. –

+0

"IDisposable to ważna część kontraktu IFunctionality" - nadal brzmi to jak mieszanie implementacji w interfejsy. Ale próbka jest zbyt abstrakcyjna, by to stwierdzić. –

+0

Ważne jest, aby konsumenci 'IFunalizmu' dysponowali dostarczoną instancją podczas zamykania. Więc w zasadzie 'IFunputeness' ma metodę wyłączania instancji i postanowiłem użyć' IDisposable.Dispose' w tym celu. Wszelkie opinie na temat alternatywnych sposobów zaprojektowania tego są bardzo doceniane, ale myślę, że powinno to następnie przejść do innego pytania. –