Curiosidade ReSharper: “O parâmetro é usado apenas para verificação (ões) de pré-condição.”

102

Por que o ReSharper está me julgando por este código?

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

O parâmetro "settingValue" do segundo método ValidateCorrespondingValueType fica esmaecido com a seguinte mensagem do ReSharper: "O parâmetro 'settingValue' é usado apenas para verificações de pré-condição."

Cadáver
fonte
Você pode mover a declaração e atribuição de exceptionMessagepara o if-block :)
AakashM
Você também pode fazer isso no método: expectedText + = ""; e para de reclamar desde que você o utilizou no método.
PHPGuru

Respostas:

104

Não está julgando, está tentando ajudar :)

Se ReSharper perceber que um parâmetro é usado apenas como uma verificação para lançar uma exceção, ele o tornará cinza, indicando que você não o está usando para trabalho "real". Provavelmente é um erro - por que passar um parâmetro que você não vai usar? Geralmente indica que você o usou em uma pré-condição, mas depois se esqueceu (ou não precisa mais) usá-lo em outro lugar no código.

Como o método é um método de asserção (ou seja, tudo o que ele faz é afirmar que é válido), você pode suprimir a mensagem marcando o ValidateCorrespondingValueTypecomo um método de asserção, usando os atributos de anotação do ReSharper , especificamente o [AssertionMethod]atributo:

[AssertionMethod]
private void ValidateCorrespondingValueType(SupportedType supportedType, object settingValue)
{
  // …
}
cidadão
fonte
3
É uma boa verificação, mas neste caso R # superou um pouco, você não acha? O tipo de verificação settingValuenão pode ser uma pré- condição, uma vez que o que está sendo verificado não é conhecido até que algum trabalho tenha sido feito dentro do corpo do método!
AakashM de
6
É por isso que você precisa dizer ao ReSharper que é um método de asserção. O único ponto desse método é realizar a verificação de pré-condição para outro método. É uma afirmação, mas o ReSharper não pode saber disso a menos que você diga com [AssertionMethod].
citizenmatt
10
Acabei mudando apenas a Severidade da Inspeção para "Não Mostrar", essa é outra opção.
reggaeguitar
61
Pode ter sido um recurso útil, se alguém pudesse desabilitar a inspeção 'somente pré-condição' independentemente da inspeção regular de parâmetros não utilizados; do jeito que está, a inspeção mistura dois problemas de gravidade diferente e geralmente torna essa funcionalidade inútil em certas situações. Também sou muito cético quanto à ideia de adicionar comentários ou atributos ao código apenas para manter uma ferramenta de análise de código-fonte feliz, então, no momento, não acho que haja uma solução satisfatória para o problema.
Serge Belov,
7
Pode estar tentando ajudar, mas é muito agressivo. Agora, se você verificar o valor e nunca usá-lo, tudo bem, provavelmente é um erro. No entanto, ele está latindo para mim sobre um em que apenas uso o valor no erro. De que outra forma isso pode ser qualquer coisa menos intencional?
Loren Pechtel
20

Curiosamente, o ReSharper recua se você usar a nova nameoffuncionalidade em 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
fonte
3
esta resposta me convém, é menos intrusiva do que adicionar um pacote
nuget
8

O seguinte corrige o problema (no ReSharper 2016.1.1, VS2015), mas não tenho certeza se ele resolve o problema 'certo'. Em qualquer caso, mostra a ambiguidade na mecânica do ReSharper em relação a este tópico:

Isso produz o aviso:

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

Mas isso não:

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

É interessante que o código equivalente (a inversão foi feita por ReSharper: D) dá resultados diferentes. Parece que a correspondência de padrões simplesmente não pega a segunda versão.

Maryn
fonte
6

Minha solução preferida para esse problema é fazer o resharper pensar que o parâmetro é usado. Isto tem uma vantagem sobre usando um atributo como UsedImplicitlyporque se alguma vez você não parar de usar esse parâmetro, ReSharper irá começar avisando novamente. Se você usar um atributo, o resharper também não detectará avisos reais futuros.

Uma maneira fácil de fazer o resharper pensar que o parâmetro está sendo usado é substituindo throwpor um método. Então, em vez de ...

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

...você escreve:

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

Isso é muito bem autodocumentado para futuros programadores e o resharper para de reclamar.

A implementação de ThrowPreconditionViolation é trivial:

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

Um método de extensão no Exception é a poluição do namespace, mas é bastante contido.

Eamon Nerbonne
fonte
+1 para mencionar [UsedImplicitly], eu não queria usar [AssertionMethod]como não era, e usado implicitamente soa mais preciso no meu caso (eu estava passando um valor para um retorno de chamada em um construtor e retornando o objeto construído).
MrLore
1

Outros já responderam à pergunta, mas ninguém mencionou as seguintes maneiras de desligar o aviso.

Adicione isso acima da assinatura do método para desativá-lo apenas para aquele método:

    // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local

Adicione isso acima da declaração de classe para desativá-lo para todo o arquivo:

     // ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local
todji
fonte
Uma desvantagem é que você não pode especificar o parâmetro que você quer dizer.
comecme
1
@comecme Você pode desabilitar para um único parâmetro usando desabilitar e restaurar comentários em torno desse parâmetro específico. Eu sugeriria colocar cada parâmetro em sua própria linha nesse caso; ainda feio, mas menos (na minha opinião).
Travis