Czy są jakieś powody, aby korzystać z prywatnych nieruchomości w C #?


246

Właśnie zdałem sobie sprawę, że konstrukcja właściwości C # może być również używana z modyfikatorem dostępu prywatnego :

private string Password { get; set; }

Chociaż jest to technicznie interesujące, nie mogę sobie wyobrazić, kiedy z niego skorzystam, ponieważ prywatne pole wymaga jeszcze mniej ceremonii :

private string _password;

i nie mogę sobie wyobrazić, kiedy kiedykolwiek będę musiał być w stanie uzyskać wewnętrznie, ale nie ustawić lub ustawić, ale nie uzyskać prywatnego pola:

private string Password { get; }

lub

private string Password { set; }

ale być może istnieje przypadek użycia z klasami zagnieżdżonymi / dziedziczonymi lub może gdzie get / set może zawierać logikę zamiast po prostu zwracać wartość właściwości, chociaż starałbym się zachować właściwości ściśle proste i pozwolić jawnym metodom na wykonywanie dowolnej logiki, np GetEncodedPassword().

Czy ktoś korzysta z własności prywatnych w języku C # z jakiegokolwiek powodu, czy jest to tylko jedna z tych technicznie możliwych, a jednak rzadko używanych w rzeczywistych konstrukcjach kodu?

Uzupełnienie

Dobre odpowiedzi, czytając je Wyrzuciłem te zastosowania dla prywatnych nieruchomości:

  • kiedy pola prywatne muszą być ładowane leniwie
  • gdy pola prywatne wymagają dodatkowej logiki lub są obliczonymi wartościami
  • ponieważ prywatne pola mogą być trudne do debugowania
  • w celu „przedstawienia sobie umowy”
  • do wewnętrznej konwersji / uproszczenia odsłoniętej właściwości w ramach serializacji
  • zawijanie zmiennych globalnych do wykorzystania w twojej klasie

Technika wspierana przez prywatne nieruchomości to samo kapsułkowanie - patrz: sourcemaking.com/refactoring/self-encapsulate-field
LBushkin

Odpowiedzi:


212

Używam ich, jeśli potrzebuję buforować wartość i chcę leniwie ją załadować.

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}

42
ładny wspólny wzór toreturn _password ?? (_password = CallExpensiveOperation());
Marc

1
@EvAlex Lazy <T> może być preferowany, jeśli możesz go użyć. Ale może używać tylko rzeczy statycznych, więc nie można uzyskać dostępu do metod (innych niż statyczne) ani innych elementów. Nawiasem mówiąc, od dłuższego czasu wolę składnię jednowierszową return _password ?? (_password = CallExpensiveOperation());. A właściwie Resharper woli :).
Bart

4
@Marc od C # 6, nawet nie musisz pisać get i return. private string Password => _password ?? (_password = CallExpensiveOperation());
Otto Abnormalverbraucher

1
W wersji C # 8 jest jeszcze krótszy:private string Password => _password ??= CallExpensiveOperation();
Dave M

2
@Bas To nie jest leniwe ładowanie, ponieważ CallExpensiveOperation();jest wywoływane podczas budowy / inicjalizacji zawierającego obiekt, a nie przy pierwszym dostępie do właściwości.
Stefan Podskubka

142

Głównym zastosowaniem tego w moim kodzie jest leniwa inicjalizacja, jak wspomnieli inni.

Innym powodem własności prywatnej nad polami jest to, że właściwości prywatne są znacznie łatwiejsze do debugowania niż prywatne pola. Często chcę wiedzieć, że „to pole jest ustawiane nieoczekiwanie; kto jest pierwszym dzwoniącym, który ustawia to pole?” i jest o wiele łatwiej, jeśli możesz po prostu ustawić punkt przerwania na seterze i nacisnąć go. Możesz tam zalogować się. Możesz tam umieścić wskaźniki wydajności. Można wprowadzić kontrole spójności, które działają w kompilacji debugowania.

Zasadniczo sprowadza się to do: kod jest znacznie potężniejszy niż dane . Każda technika, która pozwala mi napisać potrzebny kod, jest dobra. Pola nie pozwalają na pisanie w nich kodu, właściwości tak.


5
Czy Twoim zdaniem „kod jest znacznie potężniejszy niż dane”? Googlowanie zwraca referencje wskazujące na Ciebie. Chcę tylko wiedzieć, żebym mógł to poprawnie zacytować, kiedy zajdzie taka potrzeba.
Joan Venge

24
@Joan: Nie wiem. Albo to wymyśliłem, albo usłyszałem, jak ktoś inny to powiedział i pomyślałem: „wow, powinienem to całkowicie ukraść, a potem zapomnieć o tym, od kogo to ukradłem”.
Eric Lippert,

1
+ CodeLens poinformuje cię, do której właściwości się odwołuje
UuDdLrLrSs

43

być może istnieje przypadek użycia z klasami zagnieżdżonymi / dziedziczonymi lub może gdzie get / set może zawierać logikę zamiast po prostu zwracać wartość właściwości

Osobiście korzystam z tego, nawet jeśli nie potrzebuję logiki na obiekcie pobierającym lub ustawiającym właściwość. Korzystanie z właściwości, nawet prywatnej, pomaga zabezpieczyć kod na przyszłość, aby w razie potrzeby dodać logikę do modułu pobierającego.

Jeśli uważam, że właściwość może w końcu wymagać dodatkowej logiki, czasami zawijam ją do własności prywatnej zamiast używać pola, aby nie musiałem później zmieniać kodu.


W przypadku częściowo powiązanym (choć innym niż twoje pytanie) bardzo często używam prywatnych ustawień w publicznych obiektach:

public string Password 
{
    get; 
    private set;
}

To daje publiczną metodę pobierania, ale zachowuje prywatność setera.


+1 ma sens: „Jeśli uważam, że właściwość może w końcu wymagać dodatkowej logiki, czasami zawijam ją do własności prywatnej zamiast korzystać z pola, aby nie musiałem później zmieniać kodu”.
Edward Tanguay

7
prywatni setery <3
Earlz

20

Dobrym zastosowaniem dla prywatnych właściwości tylko uzyskać są wartości obliczone. Kilka razy miałem właściwości, które są prywatne tylko do odczytu i po prostu wykonuję obliczenia na innych polach mojego typu. Nie jest wart metody i nie jest interesujący dla innych klas, więc jest to własność prywatna.


20

Leniwa inicjalizacja to jedno miejsce, w którym mogą być schludne, np

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

Następnie możesz napisać: this.MyTypewszędzie zamiast this.mytype.Valuei podsumowując fakt, że jest on leniwie utworzony w jednym miejscu.

Jedną rzeczą, która szkoda, jest to, że C # nie obsługuje przestawiania zakresu pola właściwości na właściwość (tj. Deklarowanie go w definicji właściwości) w celu całkowitego ukrycia go i zapewnienia, że ​​można uzyskać do niego dostęp tylko za pośrednictwem właściwości.


2
Zgodzono się, że będzie tam aspekt scopingu.
Chris Marisic

5
Często używam tej samej techniki, a także żałuję, że pole nie może być ograniczone do treści kodu. To miła funkcja, ale niski priorytet.
Eric Lippert,

5
@Eric Lippert - field-declarationy scoped ciągu accessor-declarationsma miejsce numer 1 na mojej liście życzeń C # przez długi czas. Jeśli mógłbyś to zaprojektować i wdrożyć w jakiejś (rzeczywistej) przyszłej wersji, to upiec ci ciasto.
Jeffrey L Whitledge

13

Jedyne użycie, jakie mogę wymyślić

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}

+1 za użyteczną klasę właściwości obliczanych na podstawie innych zmiennych prywatnych
Daren Thomas

2
Dlaczego nie skorzystać z prywatnej metodyprivate bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman

10

Właściwości i pola nie są jeden do jednego. Właściwość dotyczy interfejsu klasy (czy to mówiąc o interfejsie publicznym, czy wewnętrznym), podczas gdy pole dotyczy implementacji klasy. Właściwości nie powinny być postrzegane jako sposób na ujawnienie pól, powinny być postrzegane jako sposób na ujawnienie intencji i celu klasy.

Tak jak korzystasz z nieruchomości w celu przedstawienia konsumentom umowy dotyczącej tego, co stanowi twoją klasę, możesz również przedstawić sobie umowę z bardzo podobnych powodów. Tak, używam własności prywatnych, kiedy ma to sens. Czasami własność prywatna może ukryć szczegóły implementacji, takie jak leniwe ładowanie, fakt, że właściwość jest tak naprawdę konglomeratem kilku pól i aspektów, lub że właściwość musi zostać wirtualnie utworzona przy każdym wywołaniu (pomyśl DateTime.Now). Zdecydowanie są chwile, kiedy sensowne jest wymuszanie tego nawet na sobie w zapleczu klasy.


+1: „możesz również przedstawić sobie kontrakt z bardzo podobnych powodów” ma sens
Edward Tanguay

8

Używam ich w serializacji, z rzeczami takimi jak DataContractSerializerlub protobuf-net, które obsługują to użycie ( XmlSerializernie). Przydaje się, gdy trzeba uprościć obiekt w ramach serializacji:

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}

6

Jedną rzeczą, którą robię przez cały czas, jest przechowywanie „globalnych” zmiennych / pamięci podręcznej w HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}

5

Używam ich od czasu do czasu. Mogą ułatwić debugowanie rzeczy, gdy można łatwo wprowadzić punkt przerwania we właściwości lub dodać instrukcję rejestrowania itp.

Może być również przydatny, jeśli później będziesz musiał w jakiś sposób zmienić typ swoich danych lub będziesz musiał użyć refleksji.


Tak samo; jeśli w get / set jest zaangażowana logika, czasami mogę użyć własności prywatnej lub chronionej. Zasadniczo zależy od tego, ile logiki: prostą logikę zrobię we właściwości, dużo logiki zwykle użyję funkcji pomocniczej. Cokolwiek czyni kod najbardziej łatwym w utrzymaniu.
TechNeilogy

5

Korzystam z właściwości prywatnych w celu ograniczenia kodu dostępu do właściwości podrzędnych, z których często korzystam.

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

Jest to przydatne, jeśli istnieje wiele właściwości podrzędnych.


2

Powszechną praktyką jest modyfikowanie członków tylko metodami get / set, nawet prywatnymi. Logika tego jest taka, że ​​wiesz, że get / set zawsze zachowuje się w określony sposób (na przykład odpalanie zdarzeń), co nie wydaje się mieć sensu, ponieważ nie zostaną one uwzględnione w schemacie właściwości ... ale stare nawyki umierają ciężko.


2

Ma to sens, gdy istnieje logika związana z zestawem właściwości lub get (pomyśl leniwa inicjalizacja), a właściwość jest używana w kilku miejscach w klasie.

Czy to tylko proste pole zaplecza? Nic nie przychodzi mi na myśl jako dobry powód.


2

Wiem, że to pytanie jest bardzo stare, ale poniższe informacje nie zawierały żadnej z aktualnych odpowiedzi.

Nie mogę sobie wyobrazić, kiedy kiedykolwiek będę musiał być w stanie dostać się wewnętrznie, ale nie ustawić

Jeśli wstrzykujesz swoje zależności, możesz chcieć mieć Gettera na Właściwość, a nie Settera, ponieważ oznaczałoby to właściwość tylko do odczytu. Innymi słowy Właściwość może być ustawiona tylko w konstruktorze i nie może być zmieniona przez żaden inny kod w klasie.

Również Visual Studio Professional poda informacje o właściwości, a nie polu, dzięki czemu łatwiej będzie zobaczyć, jakie pole jest używane.

PorpField


1

Jak nikt nie wspomniał, możesz użyć go do sprawdzania poprawności danych lub blokowania zmiennych.

  • Uprawomocnienie

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • Zamykający

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    Uwaga: Podczas blokowania odniesienia nie blokujesz dostępu do elementów obiektu, do którego istnieje odwołanie.

Leniwe odniesienie: Podczas leniwego ładowania może się okazać, że trzeba go wykonać asynchronicznie, dla którego obecnie istnieje AsyncLazy . Jeśli korzystasz ze starszych wersji niż Visual Studio SDK 2015 lub go nie używasz, możesz również użyć AsyncLazy AsyncEx .


0

Niektóre bardziej egzotyczne zastosowania wyraźnych pól obejmują:

  • musisz użyć reflub outz wartością - być może dlatego, że jest to Interlockedlicznik
  • jest on przeznaczony do reprezentowania fundamentalny układ na przykład nastruct z wyraźną układ (może mapować do C ++ wysypisko, albounsafe kod)
  • historycznie ten typ był używany z BinaryFormatterautomatyczną obsługą pól (zmiana na auto-rekwizyty zmienia nazwy, a tym samym psuje serializator)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.