Metoda zanieczyszczona jest wywoływana dla pola tylko do odczytu

84

Używam programu Visual Studio 2010 + Resharper i wyświetla ostrzeżenie dotyczące następującego kodu:

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

rectto readonly Rectanglepole, a Resharper pokazuje mi to ostrzeżenie:

„Nieczysta metoda jest wywoływana dla pól tylko do odczytu typu wartości”.

Jakie są nieczyste metody i dlaczego jest mi pokazywane to ostrzeżenie?

Kwaśny
źródło

Odpowiedzi:

96

Po pierwsze, odpowiedzi Jona, Michaela i Jareda są zasadniczo poprawne, ale mam jeszcze kilka rzeczy, które chciałbym do nich dodać.

Co należy rozumieć pod pojęciem „nieczystej” metody?

Łatwiej jest scharakteryzować czyste metody. „Czysta” metoda ma następujące cechy:

  • Jego wynik jest całkowicie określony przez jego dane wejściowe; jego wydajność nie zależy od czynników zewnętrznych, takich jak pora dnia lub ilość bitów na dysku twardym. Jej dorobek nie zależy od historii; dwukrotne wywołanie metody z podanym argumentem powinno dać ten sam wynik.
  • Czysta metoda nie powoduje żadnych obserwowalnych mutacji w otaczającym ją świecie. Czysta metoda może zdecydować się na mutację stanu prywatnego ze względu na efektywność, ale czysta metoda nie zmienia, powiedzmy, pola swojego argumentu.

Na przykład Math.Costo czysta metoda. Jego wyjście zależy tylko od jego wejścia, a wejście nie jest zmieniane przez wywołanie.

Nieczysta metoda to metoda, która nie jest czysta.

Jakie są niebezpieczeństwa związane z przekazywaniem struktur tylko do odczytu do nieczystych metod?

Przychodzą mi do głowy dwie. Pierwszą jest ta wskazana przez Jona, Michaela i Jareda i przed nią ostrzega cię Resharper. Kiedy wywołujesz metodę na strukturze, zawsze przekazujemy odniesienie do zmiennej, która jest odbiornikiem, na wypadek gdyby metoda chciała zmutować zmienną.

A co, jeśli wywołasz taką metodę na wartości, a nie na zmiennej? W takim przypadku tworzymy zmienną tymczasową, kopiujemy do niej wartość i przekazujemy referencję do zmiennej.

Zmienna tylko do odczytu jest uważana za wartość, ponieważ nie można jej mutować poza konstruktorem. Więc kopiujemy zmienną do innej zmiennej, a nieczysta metoda prawdopodobnie powoduje mutację kopii, jeśli zamierzasz zmutować zmienną.

Na tym polega niebezpieczeństwo przekazania struktury tylko do odczytu jako odbiorcy . Istnieje również niebezpieczeństwo przekazania struktury zawierającej pole tylko do odczytu. Struktura zawierająca pole tylko do odczytu jest powszechną praktyką, ale zasadniczo polega ona na wypisywaniu czeku, że system typów nie ma środków do zrealizowania; „Tylko do odczytu” konkretnej zmiennej określa właściciel magazynu. Instancja typu referencyjnego „posiada” własną pamięć, ale instancja typu wartościowego nie!

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

Wydaje się, że this.xto się nie zmieni, ponieważ x jest polem tylko do odczytu i Badnessnie jest konstruktorem. Ale...

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

... jasno pokazuje, że to fałsz. thisi sodnoszą się do tej samej zmiennej, a ta zmienna nie jest tylko do odczytu!

Eric Lippert
źródło
Wystarczająco uczciwe, ale proszę wziąć pod uwagę ten kod: struct Id { private readonly int _id; public Id(int id) { _id = id; } public int ToInt() => _id; } Dlaczego ToInt jest nieczyste?
boskicthebrain
@boskicthebrain: Czy twoje pytanie to „dlaczego Resharper uważa to za nieczyste?” Jeśli to jest twoje pytanie, znajdź kogoś, kto pracuje nad R # i zapytaj go!
Eric Lippert
3
Resharper da to ostrzeżenie, nawet jeśli metoda jest nieważna i nie robi nic poza return. Na tej podstawie zgaduję, że jedynym kryterium jest to, czy metoda ma [Pure]atrybut.
bornfromanegg
Stwierdziłem, że stwierdzenie „zawsze przekazujemy odniesienie do zmiennej, która jest odbiornikiem” jest dla mnie nieco zagmatwane. W przypadku OP, do czego odnosi się „zmienna”? Zakładam, że to rect. Czy mówimy, że kopia rectjest przekazywana do Containsmetody?
xtu
51

Nieczysta metoda to taka, która nie gwarantuje pozostawienia wartości tak, jak była.

W .NET 4 możesz dekorować metody i typy, [Pure]aby zadeklarować je jako czyste, a R # zwróci na to uwagę. Niestety, nie możesz zastosować go do innych członków i nie możesz przekonać R #, że typ / element członkowski jest czysty w projekcie .NET 3.5, o ile wiem. (To gryzie mnie cały czas w Noda Time .)

Idea jest taka, że jeśli wywołanie metody, która mutuje zmienna, ale to nazwać na pole tylko do odczytu, to chyba nie robi tego, co chcesz, więc R # ostrzeże Cię o tym. Na przykład:

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

Byłoby to naprawdę użyteczne ostrzeżenie, gdyby każda metoda, która była rzeczywiście czysta, została zadeklarowana w ten sposób. Niestety tak nie jest, więc jest wiele fałszywych alarmów :(

Jon Skeet
źródło
Czy to oznacza, że ​​nieczysta metoda może zmienić podstawowe pola przekazanego do niej zmiennego typu wartości?
Kwaśne
@Acidic: nie wartość argumentu - nawet nieczysta metoda może to zrobić - ale wartość, do której ją wywołujesz . (Zobacz mój przykład, w którym metoda nie ma nawet żadnych parametrów.)
Jon Skeet
2
Możesz użyć JetBrains.Annotations.PureAttributezamiast System.Diagnostics.Contracts.PureAttribute, mają to samo znaczenie dla analizy kodu ReSharper i powinny działać tak samo na .NET 3.5, .NET 4 lub Silverlight. Możesz również zewnętrznie dodawać adnotacje do zespołów, których nie jesteś właścicielem, używając plików XML (spójrz na katalog ExternalAnnotations w ścieżce bin ReSharper), może to być naprawdę przydatne!
Julien Lebosquain
5
@JulienLebosquain: Byłbym bardzo niechętny do dodawania adnotacji specyficznych dla narzędzia - szczególnie w przypadku projektu open source. Dobrze wiedzieć jako opcja, ale ...
Jon Skeet
1
Właściwie stwierdziłem, że System.Diagnostics.Contracts.PureAttributenie usunęło tego ostrzeżenia w R # 8.2, podczas gdy JetBrains.Annotations.PureAttributetak było. Te dwa atrybuty mają również różne opisy: PureAtrybut kontraktów implikuje „wynik zależy tylko od parametrów”, podczas gdy JetBrains Puresugeruje „nie powoduje widocznych zmian stanu” bez wykluczania stanu obiektu używanego do obliczenia wyniku. (Ale nadal kontrakty, które Purenie mają takiego samego wpływu na to ostrzeżenie, prawdopodobnie są błędem.)
Wormbo,
15

Krótka odpowiedź brzmi, że jest to fałszywie pozytywny wynik i możesz bezpiecznie zignorować ostrzeżenie.

Dłuższą odpowiedzią jest to, że dostęp do typu wartości tylko do odczytu tworzy jego kopię , tak że wszelkie zmiany wartości wprowadzone przez metodę wpływałyby tylko na kopię. ReSharper nie zdaje sobie sprawy, że Containsjest to czysta metoda (co oznacza, że ​​nie ma skutków ubocznych). Eric Lippert mówi o tym tutaj: Mutating Readonly Structs

Michael Liu
źródło
2
Nigdy nie ignoruj ​​tego ostrzeżenia, dopóki w pełni nie zrozumiesz !!! Dobrym przykładem gdzie może bezpiecznie ciebie jest ten konstrukt: private readonly SpinLock _spinLock = new SpinLock();- taka blokada będzie całkowicie bezużyteczny (od readonly modyfikujących przyczyn on-the-fly kopia być tworzone za każdym razem Wprowadź metodę nazywa się na nim)
Jan
11

Wygląda na to, że Reshaprer wierzy, że metoda Containsmoże zmienić rectwartość. Ponieważ rectjest readonly structkompilatorem C #, który tworzy kopie obronne wartości, aby zapobiec mutowaniu readonlypola przez metodę . Zasadniczo końcowy kod wygląda następująco

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

Resharper ostrzega cię tutaj, że Containsmoże mutować rectw sposób, który zostałby natychmiast utracony, ponieważ zdarzyło się to tymczasowo.

JaredPar
źródło
Więc nie wpłynęłoby to na żadną logikę wykonywaną w metodzie, a jedynie uniemożliwiłoby jej zmianę wartości, do której została wywołana, prawda?
Kwaśne
5

Metoda zanieczyszczona to metoda, która może mieć skutki uboczne. W tym przypadku Resharper wydaje się sądzić, że może się to zmienić rect. Prawdopodobnie nie, ale łańcuch dowodów jest zerwany.

Henk Holterman
źródło