Kiedy używać [Pure] na konstruktorze?

19

Uczę się o kontraktach kodowych w .NET i staram się zrozumieć ideę czystych konstruktorów. Dokumentacja kontraktów kodowych stanowi:

Wszystkie metody wywoływane w ramach umowy muszą być czyste; oznacza to, że nie mogą aktualizować żadnego istniejącego stanu. Metoda czysta może modyfikować obiekty utworzone po wejściu do metody czystej.

A PureAttributedokumentacja stwierdza:

Wskazuje, że typ lub metoda jest czysta, to znaczy nie wprowadza żadnych widocznych zmian stanu.

Rozumiem te stwierdzenia, jeśli chodzi o metody, ale co z konstruktorami? Załóżmy, że miałeś taką klasę:

public class Foo
{
    public int Value { get; set; }

    public Foo(int value) {
        this.Value = value;
    }
}

Ten konstruktor oczywiście wpływa na stan nowego Fooobiektu, ale nie ma innych skutków ubocznych (np. Nie manipuluje żadnym parametrem ani nie wywołuje metod nieczystych). Czy to kandydat na [Pure]czy nie? Jakie znaczenie ma umieszczenie [Pure]atrybutu na konstruktorze i kiedy powinienem to zrobić we własnym kodzie?

pswg
źródło

Odpowiedzi:

14

Dekorujesz metodę za pomocą [Pure]:

  • Jeśli metoda nie ma skutków ubocznych. Na przykład, jeśli metoda uzyskuje dostęp do bazy danych i modyfikuje ją lub jej wynik zależy od bazy danych, nie jest ona czysta.

  • A jeśli spodziewasz się, że użyjesz go w kontraktach kodowych. Na przykład, jeśli metoda jest czysta , ale nie masz zamiaru jej używać w kontraktach kodowych, dodanie [Pure]nie przyniosłoby korzyści i nie przyspieszy twojego kodu.

O ile dotyczy to konstruktorów, wygląda na to, że są Zakłada się być czysty w .NET i nie potrzebują wyraźnego atrybut. Patrzyłem na kilka konstruktorów w źródle .NET Framework, takich jak DateTime, i nie mają one [Pure]atrybutu.

Przypuszczam, że dzieje się tak z kilku powodów:

  • Może być zbyt niepraktyczne, aby napisać konstruktora bez parametrów z [Pure]atrybutem, aby móc użyć klasy / struktury w kontrakcie.

  • Niektóre, na przykład String, nie mają jawnych konstruktorów.

  • Konstruktorzy są traktowani specjalnie, nawet poza umowami kodowymi; na przykład nie oczekuje się od nich wprowadzania wyjątków .

  • [Pure]to tylko konwencja, która ma uprościć twoje życie, ale nie ma rzeczywistej kontroli statycznej, aby upewnić się, że metoda ozdobiona tym atrybutem jest czysta. void DestroyDatabase()mogą być udekorowane jako czyste, a umowy kodowe nie zauważą niczego złego.

    Obecnie nie ma elementu kontraktów kodowych, który sprawdza, czy metody uznane za czyste są rzeczywiście czyste. Tak więc, jeśli programista ozdobił metodę [Pure], to po prostu się w to wierzy.

    Z umów nr 5: Czystość metody

  • Sam program .NET Framework zawiera konstruktory, które nie są czyste. Na przykład List<T>(IEnumerable<T> collection)jest w rzeczywistości nieczyste, jeśli przechodzenie przez kolekcję ma skutki uboczne.

  • Kontrakty krzyczą być proste. Mogę łatwo wyobrazić sobie taką umowę Contract.Requires(!string.IsNullOrEmpty(name)), więc istnieją dobre powody, aby uznać statyczną string.IsNullOrEmptyczystość.

    Z drugiej strony, jeśli potrzebujesz StringBuilder, aby zbudować ciąg, sprawdzisz wtedy coś, wywołując metodę instancji klasy biznesowej, prawdopodobnie niewłaściwie wykorzystujesz umowy. Dlatego też StringBuilder.ToStringnie jest oznaczony jako czysty, nawet jeśli może być (prawda?)

Arseni Mourzenko
źródło
Kontroler kontraktów przyjmuje wiele kontraktów kodowych na typy systemów, w tym „ każdą metodę, której pełna nazwa zaczyna się od„ System.Diagnostics.Contracts.Contract ”,„ System.String ”,„ System.IO.Path ”lub„ System . Wpisz „ ”. Niestety nie jestem pewien, czy przeglądanie typów .NET jest zbyt przydatne, jeśli chodzi o kontrakty kodowe.
pswg
3
Czystość jest jedną z tych rzeczy, w których cały wywoływany kod również musi być czysty, lub wywołujący nie jest „czysty”. Trudno mi uwierzyć, że wszystkie konstruktory są domyślnie uważane za czyste.
Frank Hileman
@FrankHileman: ja też. Nie mam teraz kompilatora C #, ale wystarczyłoby napisać klasę z konstruktorem i bez [Pure]atrybutu, i użyć go gdzieś w kontrakcie, aby uzyskać ostateczną odpowiedź.
Arseni Mourzenko
1

Nie można użyć obiektu, dopóki nie zostanie skonstruowany w tym przypadku. Dlatego konstruktor jest czysty. Gdyby konstruktor wywołał inny kod lub wywołał delegata, a inny kod zmodyfikowałby zmienną właściwość, nie byłoby to czyste. Aby być bezpieczniejszym, lepiej jest uczynić nieruchomość niezmienną.

Frank Hileman
źródło
Czysty konstruktor to czysta metoda, która może zmienić stan obecnej klasy, o ile spełnia pozostałe warunki bycia czystym? BTW, Właściwość jest zmienna, ponieważ chciałem podkreślić, że sama klasa nie jest czysta.
pswg
@pswg: Utworzyłeś interesujące pytanie, na które prawdopodobnie musi odpowiedzieć Microsoft. Załóżmy, że konstruktor wywołał metodę, która zmodyfikowała zmienną właściwość; czy konstruktor nadal byłby czysty? Myślę, że technicznie tak nie byłoby, mimo że modyfikacja jest „niewidoczna” dla zewnętrznych widzów. Ponieważ w twoim oryginalnym przykładzie nie przywołano żadnego innego kodu, musi on być czysty według dowolnej definicji, jaką mogę wymyślić.
Frank Hileman
@pswg: Tyle że zestaw właściwości jest również wywołaniem metody. Myślę, że powinieneś zapytać na forach MSDN.
Frank Hileman
Wydaje mi się, że jeśli główną ideą czystości jest to, czy metoda wprowadza jakieś obserwowalne zmiany, to w tym sensie, niezależnie od tego, czy wywołuje metody nieczyste, o ile zmiany nie mogą być zaobserwowane przez żadnego dzwoniącego, nadal byłaby to czysta metoda.
pswg
@pswg: To jest abstrakcyjna definicja. Ale gdybym pisał analizator dla tych rzeczy, prawdopodobnie rozważane byłoby również wywołanie metody nieoczyszczonej, aby uczynić osobę wywołującą nieczystą. Tylko dla uproszczenia wdrożenia. Zatem pytanie brzmi: czy konstruktor jest normalnym wywołaniem metody lub jak głęboko sięga analiza.
Frank Hileman