2012-03-29 3 views
71

Я использую Visual Studio 2010 + ReSharper и это показывает предупреждение на следующий код:Impure метод вызывается только для чтения поля

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

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

«Метод Impure вызывается для поля типа readonly типа значения».

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

+0

http://www.minddriven.de/index.php/technology/dot-net/code-contracts/code-contracts-method-purity – siride

ответ

81

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

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

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

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

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

Нечистый метод - это метод, который не является чистым.

Каковы некоторые из опасностей передачи readonly структур на нечистые методы?

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

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

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

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

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 не изменится, потому что х является только для чтения поле и Badness не является конструктором. Но ...

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

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

+0

Достаточно справедливо, но учтите этот код: 'struct Id {' 'private readonly int _id;' 'public Id (int id) {_id = id; } ' ' public int ToInt() => _id; ' '} ' Почему * ToInt * нечисто? – boskicthebrain

+0

@ boskicthebrain: Действительно ли ваш вопрос «почему Решарпер считает это нечистым?» Если это ваш вопрос, то найдите того, кто работает на R # и спрашивает их! –

+0

Resharper выдаст это предупреждение, даже если метод недействителен и ничего не делает, кроме 'return'. Исходя из этого, я предполагаю, что единственным критерием является то, имеет ли этот метод атрибут '[Pure]'. – bornfromanegg

46

Нечистый метод - это тот, который не гарантирует, что он оставит ценность как есть.

В .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 
    } 
} 

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

+0

Таким образом, это означает, что нечистый метод может изменить лежащие в основе поля изменяемого valuetype передан ему? – Acidic

+0

@Acidic: Не значение аргумента - даже нечистый метод может это сделать, - но значение, которое вы * назовете *. (См. Мой пример, в котором метод даже не имеет параметров.) –

+0

Означает ли это, что любой тип значения, который 'readonly' становится неизменным? – Acidic

5

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

11

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

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

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

+0

Таким образом, это не повлияет на любую логику, выполняемую в методе, только не позволит ей изменить значение, на которое оно было вызвано, не так ли? – Acidic

+1

@ Правильно. – JaredPar

13

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

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

+1

Пожалуйста, никогда не игнорируйте это предупреждение до полного понимания !!! Одним из хороших примеров, где это может быть безопасно, является эта конструкция: 'private readonly SpinLock _spinLock = new SpinLock();' - такая блокировка будет абсолютно бесполезной (поскольку модификатор readonly вызывает создание копии «на лету» каждый раз, когда Enter метод вызывается на нем) – Jan

Смежные вопросы