ReSharper Curiosity: „Parametr jest używany tylko do sprawdzania warunków wstępnych”.

102

Dlaczego ReSharper ocenia mnie za ten kod?

    private Control GetCorrespondingInputControl(SupportedType supportedType, object settingValue)
    {
        this.ValidateCorrespondingValueType(supportedType, settingValue);

        switch(supportedType)
        {
            case SupportedType.String:
                return new TextBox { Text = (string)settingValue };
            case SupportedType.DateTime:
                return new MonthPicker { Value = (DateTime)settingValue, ShowUpDown = true };
            default:
                throw new ArgumentOutOfRangeException(string.Format("The supported type value, {0} has no corresponding user control defined.", supportedType));
        }
    }

    private void ValidateCorrespondingValueType(SupportedType supportedType, object settingValue)
    {
        Type type;

        switch(supportedType)
        {
            case SupportedType.String:
                type = typeof(string);
                break;
            case SupportedType.DateTime:
                type = typeof(DateTime);
                break;
            default:
                throw new ArgumentOutOfRangeException(string.Format("The supported type value, {0} has no corresponding Type defined.", supportedType));
        }
        string exceptionMessage = string.Format("The specified setting value is not assignable to the supported type, [{0}].", supportedType);
        if(settingValue.GetType() != type)
        {
            throw new InvalidOperationException(exceptionMessage);
        }
    }

Parametr „settingValue” drugiej metody ValidateCorrespondingValueType jest wyszarzony z następującym komunikatem ReSharper: „Parametr„ settingValue ”jest używany tylko do sprawdzania warunków wstępnych."

Corpsekicker
źródło
Możesz przenieść deklarację i przypisanie exceptionMessagedo if-bloku :)
AakashM
Możesz to również zrobić w metodzie: spodziewany tekst + = ""; i przestaje narzekać, ponieważ użyłeś go w metodzie.
PHPGuru

Odpowiedzi:

104

To nie ocenianie, tylko próba pomocy :)

Jeśli narzędzie ReSharper widzi, że parametr jest używany tylko jako sprawdzenie, aby zgłosić wyjątek, wyszarza go, wskazując, że w rzeczywistości nie używasz go do „prawdziwej” pracy. Najprawdopodobniej jest to błąd - po co przekazywać parametr, którego nie będziesz używać? Zwykle oznacza, że ​​użyłeś go w stanie wstępnym, ale zapomniałeś (lub już nie potrzebujesz) użyć go w innym miejscu kodu.

Ponieważ metoda jest metodą asercji (to znaczy wszystko, co robi, to potwierdza, że ​​jest prawidłowa), możesz pominąć komunikat, oznaczając ValidateCorrespondingValueTypejako metodę potwierdzenia, używając atrybutów adnotacji ReSharper , w szczególności [AssertionMethod]atrybutu:

[AssertionMethod]
private void ValidateCorrespondingValueType(SupportedType supportedType, object settingValue)
{
  // …
}
citizenmatt
źródło
3
To niezły czek, ale w tym przypadku R # trochę przekroczył, prawda? Typ sprawdzenia settingValuenie może być warunkiem wstępnym, ponieważ rzecz, względem której jest sprawdzany, nie jest znana, dopóki nie zostanie wykonana pewna praca w treści metody!
AakashM
6
Dlatego musisz powiedzieć ReSharper, że jest to metoda potwierdzania. Jedynym celem tej metody jest sprawdzenie warunków wstępnych dla innej metody. To twierdzenie, ale ReSharper nie może tego wiedzieć, chyba że powiesz o tym [AssertionMethod].
citizenmatt
10
Skończyło się na zmianie poziomu kontroli na „Nie pokazuj”, to kolejna opcja.
reggaeguitar
61
Mogłaby być użyteczna funkcja, gdyby można było wyłączyć inspekcję „tylko warunek wstępny” niezależnie od zwykłej kontroli nieużywanych parametrów; w obecnym stanie inspekcja łączy w sobie dwie kwestie o różnej wadze i ogólnie czyni tę funkcję bezużyteczną w niektórych sytuacjach. Jestem również bardzo sceptyczny co do pomysłu dodawania komentarzy lub atrybutów do kodu tylko po to, aby narzędzie do analizy kodu źródłowego było zadowolone, więc w tej chwili nie sądzę, aby było zadowalające rozwiązanie problemu.
Serge Belov
7
Może próbuje pomóc, ale jest zbyt agresywne. Teraz, jeśli zweryfikujesz wartość, a następnie nigdy jej nie użyjesz, w porządku, prawdopodobnie jest to błąd. Jednak to na mnie warczy przez jeden, w którym używam tylko wartości w błędzie. Jak inaczej może to być coś innego niż zamierzone?
Loren Pechtel
20

Co ciekawe, ReSharper wycofuje się, jeśli używasz nowej nameoffunkcjonalności w C # 6:

static void CheckForNullParameters(IExecutor executor, ILogger logger)
{
    if (executor == null)
    {
        throw new ArgumentNullException(nameof(executor));
    }

    if (logger == null)
    {
        throw new ArgumentNullException(nameof(logger));
    }
}
Holf
źródło
3
ta odpowiedź mi odpowiada, jest mniej uciążliwa niż dodanie pakietu
nuget
8

Poniższe rozwiązania rozwiązują problem (w ReSharper 2016.1.1, VS2015), ale nie jestem pewien, czy rozwiązuje „właściwy” problem. W każdym razie pokazuje to niejednoznaczność w mechanice ReSharpera w tym temacie:

Daje to ostrzeżenie:

    private void CheckForNull(object obj)
    {
        if (ReferenceEquals(obj, null))
        {
            throw new Exception();
        }
    }

Ale to nie:

    private void CheckForNull(object obj)
    {
        if (!ReferenceEquals(obj, null))
        {
            return;
        }
        throw new Exception();
    }

Co ciekawe, równoważny kod (inwersję wykonał ReSharper: D) daje inne wyniki. Wydaje się, że dopasowanie wzorców po prostu nie odbiera drugiej wersji.

Maryn
źródło
6

Moim preferowanym rozwiązaniem tego problemu jest przekonanie resharpera, że ​​parametr jest używany. To ma przewagę nad użyciem atrybutu, takie jak UsedImplicitly, bo jeśli kiedykolwiek ma przystanek przy użyciu tego parametru resharper rozpocznie ostrzeżenia ponownie. Jeśli użyjesz atrybutu, reharper również nie złapie prawdziwych ostrzeżeń w przyszłości.

Łatwym sposobem na przekonanie osoby, że parametr jest używany, jest zastąpienie throwgo metodą. Więc zamiast ...

if(myPreconditionParam == wrong)
    throw new Exception(...);

...ty piszesz:

if(myPreconditionParam == wrong)
    new Exception(...).ThrowPreconditionViolation();

Jest to przyjemne samodokumentowanie dla przyszłych programistów, a ponownie przestaje marudzić.

Implementacja ThrowPreconditionViolation jest trywialna:

public static class WorkAroundResharperBugs 
{
    //NOT [Pure] so resharper shuts up; the aim of this method is to make resharper 
    //shut up about "Parameter 'Foobaar' is used only for precondition checks" 
    //optionally: [DebuggerHidden]
    public static void ThrowPreconditionViolation(this Exception e)
    {
        throw e;
    }
}

Metoda rozszerzenia w Exception to zanieczyszczenie przestrzeni nazw, ale jest dość ograniczona.

Eamon Nerbonne
źródło
+1 za wspomnienie [UsedImplicitly], nie chciałem używać, [AssertionMethod]ponieważ nie było, i użyte domyślnie brzmi to dokładniej w moim przypadku (przekazywałem wartość do wywołania zwrotnego w konstruktorze i zwracałem skonstruowany obiekt).
MrLore
1

Inni już odpowiedzieli na pytanie, ale nikt nie wspomniał o następujących sposobach wyłączenia ostrzeżenia.

Dodaj to nad podpisem metody, aby wyłączyć ją tylko dla tej metody:

    // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local

Dodaj to powyżej deklaracji klasy, aby wyłączyć ją dla całego pliku:

     // ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local
todji
źródło
Wadą jest to, że nie możesz określić parametru, który masz na myśli.
comecme
1
@comecme Możesz wyłączyć dla pojedynczego parametru, używając opcji wyłączania i przywracania komentarzy dotyczących tego konkretnego parametru. W takim przypadku sugerowałbym umieszczenie każdego parametru w osobnej linii; nadal brzydki, ale mniej (moim zdaniem).
Travis