ReSharper Curiosity: "Параметр используется только для проверки предварительных условий".

Почему ReSharper судит меня за этот код?

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

Второй метод ValidateCorrespondingValueType "settingValue" параметр серого цвета со следующим сообщением ReSharper: "Параметр" settingValue "используется только для проверки предварительных условий."

Ответ 1

Это не судит, он пытается помочь:)

Если ReSharper видит, что параметр используется только как проверка, чтобы исключить исключение, он выгружает его, указывая, что вы на самом деле не используете его для "реальной" работы. Это, скорее всего, ошибка: зачем передавать параметр, который вы не собираетесь использовать? Обычно это означает, что вы использовали его в предварительном состоянии, но затем забыли (или больше не нужны), чтобы использовать его в другом месте в коде.

Так как метод является методом утверждения (т.е. все, что он делает, утверждается, что он действителен), вы можете подавить сообщение, пометив ValidateCorrespondingValueType как метод утверждения, используя ReSharper атрибуты аннотации, в частности атрибут [AssertionMethod]:

[AssertionMethod]
private void ValidateCorrespondingValueType(SupportedType supportedType, object settingValue)
{
  // …
}

Ответ 2

Интересно, что ReSharper отступает, если вы используете новую функциональность nameof в С# 6:

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

    if (logger == null)
    {
        throw new ArgumentNullException(nameof(logger));
    }
}

Ответ 3

Ниже приведена проблема (в ReSharper 2016.1.1, VS2015), но я не уверен, что она решает проблему "права". В любом случае, это показывает двусмысленность механики ReSharper в отношении этой темы:

Это дает предупреждение:

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

Но это не так:

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

Интересно, что эквивалентный код (инверсия была выполнена ReSharper: D) дает разные результаты. Кажется, что сопоставление шаблонов просто не поднимает вторую версию.

Ответ 4

Мое предпочтительное решение этой проблемы заключается в том, что resharper считает, что этот параметр используется. Это имеет преимущество перед использованием атрибута, такого как UsedImplicitly, потому что если вы перестанете использовать этот параметр, resharper снова начнет предупреждать вас. Если вы используете атрибут, resharper также не поймает будущих реальных предупреждений.

Легкий способ заставить resharper думать, что параметр используется, - это заменить throw на метод. Поэтому вместо...

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

... вы пишете:

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

Это красиво самодокументируемо для будущих программистов, и resharper quiting ныть.

Реализация ThrowPreconditionViolation тривиальна:

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

Метод расширения в Exception - это загрязнение пространства имен, но оно довольно содержалось.