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


83

Я використовую 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), це дійсно може бути дуже корисно!
Жульєн Лебосквен

5
@JulienLebosquain: Я б дуже не хотів починати додавати спеціальні анотації інструментів - особливо для проекту з відкритим кодом. Добре знати про це як варіант, але ...
Джон Скіт,

1
Насправді я виявив, що System.Diagnostics.Contracts.PureAttributeне придушував цього попередження у R # 8.2, хоча й JetBrains.Annotations.PureAttributeробив. Два атрибути також мають різні описи: Pureатрибут Contracts передбачає "результат залежить лише від параметрів", тоді як JetBrains Pureозначає "не викликає видимих ​​змін стану", не виключаючи стан об'єкта, який використовується для обчислення результату. (Але все-таки контракти, які Pureне мають однакового впливу на це попередження, ймовірно, є помилкою.)
Wormbo

15

Коротка відповідь полягає в тому, що це помилково позитивний результат, і ви можете сміливо ігнорувати попередження.

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


2
Будь ласка, ніколи не ігноруйте це попередження до повного розуміння !!! Одним хорошим прикладом, коли це може вас захистити, є така конструкція: private readonly SpinLock _spinLock = new SpinLock();- такий замок був би абсолютно марним (оскільки модифікатор лише для читання змушує створювати копію на льоту при кожному виклику методу Enter)
січня

11

Схоже, Решапрер вважає, що метод Containsможе мутувати rectзначення. Оскільки rectце readonly structкомпілятор С # робить захисні копії значення , щоб запобігти метод з мутує в readonlyполе. По суті остаточний код виглядає так

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

Resharper попереджає вас тут, що Containsможе мутувати rectтаким чином, що буде негайно втрачено, оскільки це сталося тимчасово.


Отже, це не вплине на жодну логіку, яка виконується в методі, лише заважає йому мутувати значення, яке було викликане, так?
Acidic

5

Нечистий метод - це метод, який може мати побічні ефекти. У цьому випадку Решарпер, здається, думає, що це може змінитися rect. Можливо, ні, але ланцюг доказів порушений.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.