Нечистый метод вызывается для поля только для чтения

84

Я использую Visual Studio 2010 + Resharper, и он показывает предупреждение о следующем коде:

if (rect.Contains(point))
{
    ...
}

rect- это readonly Rectangleполе, и Resharper показывает мне это предупреждение:

«Нечистый метод вызывается для поля типа значения только для чтения».

Что такое нечистые методы и почему мне показывают это предупреждение?

Кислая
источник

Ответы:

96

Во-первых, ответы Джона, Майкла и Джареда в основном верны, но я хотел бы добавить к ним еще несколько вещей.

Что подразумевается под «нечистым» методом?

Чистые методы охарактеризовать проще. «Чистый» метод имеет следующие характеристики:

  • Его выход полностью определяется его входом; его вывод не зависит от внешних факторов, таких как время дня или биты на вашем жестком диске. Его выход не зависит от его истории; вызов метода с заданным аргументом дважды должен дать тот же результат.
  • Чистый метод не производит наблюдаемых мутаций в окружающем мире. Чистый метод может изменить частное состояние ради эффективности, но чистый метод, скажем, не изменяет поле своего аргумента.

Например, Math.Cosэто чистый метод. Его вывод зависит только от его ввода, и ввод не изменяется при вызове.

Нечистый метод - это не чистый метод.

Каковы некоторые опасности передачи структур только для чтения нечистым методам?

На ум приходят два. На первый указал Джон, Майкл и Джаред, и именно об этом вас предупреждает Решарпер. Когда вы вызываете метод в структуре, мы всегда передаем ссылку на переменную, которая является получателем, на случай, если метод желает изменить переменную.

Так что, если вы вызовете такой метод для значения, а не для переменной? В этом случае мы создаем временную переменную, копируем в нее значение и передаем ссылку на переменную.

Переменная только для чтения считается значением, потому что ее нельзя изменить вне конструктора. Итак, мы копируем переменную в другую переменную, и нечистый метод, возможно, изменяет копию, когда вы намереваетесь изменить переменную.

Это опасность передачи структуры только для чтения в качестве получателя . Также существует опасность передачи структуры, содержащей поле только для чтения. Структура, содержащая поле только для чтения, является обычной практикой, но по сути это проверка того, что у системы типов нет средств для обналичивания; «Доступность только для чтения» конкретной переменной определяется владельцем хранилища. Экземпляр ссылочного типа «владеет» своим собственным хранилищем, а экземпляр типа значения - нет!

struct S
{
  private readonly int x;
  public S(int x) { this.x = x; }
  public void Badness(ref S s)
  {
    Console.WriteLine(this.x);   
    s = new S(this.x + 1);
    // This should be the same, right?
    Console.WriteLine(this.x);   
  }
}

Кто-то думает, что this.xэто не изменится, потому что x - это поле только для чтения, а Badnessне конструктор. Но...

S s = new S(1);
s.Badness(ref s);

... ясно демонстрирует лживость этого. thisи sотносятся к той же переменной, и эта переменная не доступна только для чтения!

Эрик Липперт
источник
Справедливо, но, пожалуйста, рассмотрите этот код: struct Id { private readonly int _id; public Id(int id) { _id = id; } public int ToInt() => _id; } Почему ToInt нечист?
boskicthebrain
@boskicthebrain: Ваш вопрос на самом деле «почему Решарпер считает это нечистым?» Если это ваш вопрос, то найдите кого-нибудь, кто работает над R #, и спросите его!
Эрик Липперт
3
Resharper выдаст это предупреждение, даже если метод недействителен и ничего не делает, кроме return. Исходя из этого, я предполагаю, что единственным критерием является наличие у метода [Pure]атрибута.
bornfromanegg
Я обнаружил, что это утверждение «мы всегда передаем ссылку на переменную, которая является получателем» меня немного сбивает с толку. Что означает «переменная» в случае с ЗП? Я предполагаю, что это файл rect. Мы говорим, что копия rectпередается Containsметоду?
xtu
51

Нечистый метод - это метод, который не гарантирует сохранение значения как было.

В .NET 4 вы можете декорировать методы и типы, [Pure]чтобы объявить их чистыми, и R # это заметит. К сожалению, вы не можете применить его к другим членам, и вы не можете убедить R #, что тип / член является чистым в проекте .NET 3.5, насколько мне известно. (Это все время кусает меня в Noda Time .)

Идея в том , что если вы вызываете метод , который мутирует переменной, но вы вызываете его на поле только для чтения, это, вероятно , не делать то , что вы хотите, так что R # предупредит вас об этом. Например:

public struct Nasty
{
    public int value;

    public void SetValue()
    {
        value = 10;
    }
}

class Test
{
    static readonly Nasty first;
    static Nasty second;

    static void Main()
    {
        first.SetValue();
        second.SetValue();
        Console.WriteLine(first.value);  // 0
        Console.WriteLine(second.value); // 10
    }
}

Это было бы действительно полезным предупреждением, если бы каждый метод, который на самом деле был чистым, был объявлен таким образом. К сожалению, это не так, поэтому ложных срабатываний много :(

Джон Скит
источник
Значит, нечистый метод может изменить базовые поля переданного ему изменяемого типа значения?
Acidic
@Acidic: не значение аргумента - даже нечистый метод может это сделать, - а значение, которое вы его вызываете . (См. Мой пример, где у метода даже нет параметров.)
Джон Скит,
2
Вы можете использовать JetBrains.Annotations.PureAttributeвместо них System.Diagnostics.Contracts.PureAttribute, они имеют то же значение для анализа кода ReSharper и должны одинаково работать в .NET 3.5, .NET 4 или Silverlight. Вы также можете добавлять внешние аннотации к сборкам, которыми вы не владеете, с помощью файлов XML (посмотрите каталог ExternalAnnotations в пути к корзине ReSharper), это действительно может быть очень полезно!
Julien Lebosquain,
5
@JulienLebosquain: Я бы очень неохотно начал добавлять аннотации для конкретных инструментов - особенно для проекта с открытым исходным кодом. Полезно знать об одном из вариантов, но ...
Джон Скит,
1
На самом деле я обнаружил, что System.Diagnostics.Contracts.PureAttributeэто предупреждение не подавлялось в R # 8.2, а подавлялось JetBrains.Annotations.PureAttribute. Эти два атрибута также имеют разные описания: Pureатрибут контрактов подразумевает «результат зависит только от параметров», тогда как JetBrains Pureподразумевает «не вызывает видимых изменений состояния», не исключая состояния объекта, используемого для вычисления результата. (Но все же контракты, Pureне оказывающие такого же эффекта на это предупреждение, вероятно, являются ошибкой.)
Wormbo
15

Короткий ответ: это ложное срабатывание, и вы можете игнорировать предупреждение.

Более длинный ответ заключается в том, что доступ к типу значения, доступному только для чтения, создает его копию , так что любые изменения значения, внесенные методом, будут влиять только на копию. ReSharper не понимает, что Containsэто чистый метод (то есть у него нет побочных эффектов). Эрик Липперт говорит об этом здесь: Мутация структур, доступных только для чтения

Майкл Лю
источник
2
Пожалуйста, никогда не игнорируйте это предупреждение, пока не поймете полностью !!! Хорошим примером, в котором это может вас обезопасить, является эта конструкция: private readonly SpinLock _spinLock = new SpinLock();- такая блокировка была бы совершенно бесполезной (поскольку модификатор readonly вызывает создание копии на лету каждый раз, когда для нее вызывается метод Enter)
Ян
11

Похоже, Решапрер считает, что метод Containsможет изменять rectзначение. Поскольку rectэто readonly structкомпилятор C # создает защитные копии значения, чтобы предотвратить изменение readonlyполя методом . По сути, окончательный код выглядит так

Rectangle temp = rect;
if (temp.Contains(point)) {
  ...
}

Resharper предупреждает вас здесь, что Containsможет измениться rectтак, что будет немедленно потеряно, потому что это произошло временно.

ДжаредПар
источник
Так что это не повлияет на логику, выполняемую в методе, а только предотвратит изменение значения, которое было вызвано, верно?
Acidic
5

Нечистый метод - это метод, который может иметь побочные эффекты. В этом случае Resharper, кажется, думает, что это может измениться rect. Вероятно, нет, но цепочка доказательств прервана.

Хенк Холтерман
источник