ReSharper Curiosity: "El parámetro solo se usa para verificaciones de condiciones previas".

102

¿Por qué ReSharper me juzga 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);
        }
    }

El parámetro "settingValue" del segundo método ValidateCorrespondingValueType está atenuado con el siguiente mensaje de ReSharper: "El parámetro 'settingValue' solo se usa para verificaciones de condiciones previas".

Corpsekicker
fuente
Puede mover la declaración y asignación de exceptionMessageen el ifbloque :)
AakashM
También puede hacer esto en el método: texto esperado + = ""; y deja de quejarse desde que lo usaste en el método.
PHPGuru

Respuestas:

104

No está juzgando, está tratando de ayudar :)

Si ReSharper ve que un parámetro solo se usa como verificación para lanzar una excepción, lo atenúa, lo que indica que en realidad no lo está usando para un trabajo "real". Lo más probable es que se trate de un error: ¿por qué pasar un parámetro que no va a utilizar? Por lo general, indica que lo usó en una condición previa, pero luego olvidó (o ya no necesita) usarlo en otra parte del código.

Dado que el método es un método de aserción (es decir, todo lo que hace es afirmar que es válido), puede suprimir el mensaje marcando ValidateCorrespondingValueTypecomo un método de aserción, utilizando los atributos de anotación de ReSharper , específicamente el [AssertionMethod]atributo:

[AssertionMethod]
private void ValidateCorrespondingValueType(SupportedType supportedType, object settingValue)
{
  // …
}
ciudadanomatt
fuente
3
Es un buen cheque, pero en este caso R # se ha excedido un poco, ¿no crees? El tipo de verificación settingValueno puede ser una condición previa , ya que la cosa contra la que se está verificando no se conoce hasta que se haya realizado algún trabajo dentro del cuerpo del método.
AakashM
6
Es por eso que necesita decirle a ReSharper que es un método de afirmación. El único objetivo de este método es realizar la comprobación de condiciones previas para otro método. Es una afirmación, pero ReSharper no puede saberlo a menos que lo diga con [AssertionMethod].
citizenmatt
10
Terminé cambiando la severidad de la inspección a "No mostrar", esta es otra opción.
reggaeguitar
61
Podría haber sido una característica útil, si se pudiera deshabilitar la inspección "solo condición previa" independientemente de la inspección regular de parámetros no utilizados; tal como está, la inspección mezcla dos problemas de diferente gravedad y, en general, hace que esta funcionalidad sea inútil en determinadas situaciones. También soy muy escéptico sobre la idea de agregar comentarios o atributos al código solo para mantener contenta a una herramienta de análisis de código fuente, así que por el momento no creo que haya una solución satisfactoria al problema.
Serge Belov
7
Puede que esté intentando ayudar, pero es demasiado agresivo. Ahora, si verifica el valor y luego nunca lo usa, bien, es probable que sea un error. Sin embargo, me está ladrando sobre uno en el que solo uso el valor en el error. ¿De qué otra manera puede eso ser algo que no sea intencional?
Loren Pechtel
20

Curiosamente, ReSharper retrocede si usa la nueva nameoffuncionalidad en 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
fuente
3
esta respuesta me conviene, es menos intrusiva que agregar un paquete
nuget
8

Lo siguiente soluciona el problema (en ReSharper 2016.1.1, VS2015), pero no estoy seguro de que resuelva el problema "correcto". En cualquier caso, muestra la ambigüedad en la mecánica de ReSharper con respecto a este tema:

Esto produce la advertencia:

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

Pero esto no:

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

Es interesante que el código equivalente (la inversión fue realizada por ReSharper: D) da resultados diferentes. Parece que la coincidencia de patrones simplemente no recoge la segunda versión.

Maryn
fuente
6

Mi solución preferida a este problema es hacer que el resharper piense que se usa el parámetro . Esto tiene una ventaja sobre el uso de un atributo, como UsedImplicitlyporque si alguna vez qué dejar de usar ese parámetro, ReSharper se iniciará un aviso de nuevo. Si usa un atributo, el resharper tampoco detectará advertencias reales futuras.

Una forma sencilla de hacer que el afilado piense que se utiliza el parámetro es sustituyéndolo throwpor un método. Entonces en lugar de ...

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

...usted escribe:

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

Esto es muy bien autodocumentado para futuros programadores, y el resharper deja de quejarse.

La implementación de ThrowPreconditionViolation es 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;
    }
}

Un método de extensión en Exception es la contaminación del espacio de nombres, pero está bastante contenido.

Eamon Nerbonne
fuente
+1 por mencionar [UsedImplicitly], no quería usarlo [AssertionMethod]ya que no lo era, y el uso implícito suena más preciso en mi caso (estaba pasando un valor a una devolución de llamada en un constructor y devolviendo el objeto construido).
MrLore
1

Otros ya han respondido a la pregunta, pero nadie mencionó las siguientes formas de desactivar la advertencia.

Agregue esto encima de la firma del método para desactivarlo solo para ese método:

    // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local

Agregue esto encima de la declaración de clase para desactivarlo para todo el archivo:

     // ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local
todji
fuente
Una desventaja es que no puede especificar el parámetro al que se refiere.
comecme
1
@comecme Puede deshabilitar para un solo parámetro utilizando deshabilitar y restaurar comentarios sobre ese parámetro en particular. Sugeriría poner cada parámetro en su propia línea en ese caso; todavía feo pero menos (en mi opinión).
Travis