Какво трябва и какво не трябва да правят Getters и Setters

Възможен дубликат:
Конвенционален въпрос: Кога използвате функция Getter/Setter, вместо да използвате свойство

Напоследък се натъкнах на много различни мнения относно Getters и Setters, така че реших, че трябва да го превърна в собствен въпрос.

Мой предишен въпрос получи незабавен коментар ( по-късно изтрито), че посочените сетери не трябва да имат никакви странични ефекти и методът SetProperty би бил по-добър избор.

Наистина, това изглежда е мнението на Microsoft също. Въпреки това, техните свойства често предизвикват събития, като Resized, когато е зададено свойство Width или Height на формуляр. OwenP също заявява "не трябва да позволявате на свойство да хвърля изключения, свойствата не трябва да имат странични ефекти, редът не трябва да има значение и свойствата трябва да се връщат сравнително бързо."

И все пак Michael Stum заявява, че трябва да се правят изключения, докато валидиране на данни в сетер. Ако вашият сетер не хвърля изключение, как бихте могли ефективно да валидирате данните, тъй като толкова много от отговорите на този въпрос предлага?

Какво ще кажете, когато трябва да повдигнете събитие, както правят почти всички Control на Microsoft? Тогава не сте ли на милостта на всеки, който се е абонирал за вашето събитие? Ако техният манипулатор изпълни огромно количество информация или сам хвърли грешка, какво се случва с вашия сетер?

И накрая, какво ще кажете за мързеливо зареждане в гетъра? Това също може да наруши предишните насоки.

Какво е приемливо да се постави в getter или setter и какво трябва да се запази само в методите за достъп?

Редактиране:

От друга статия в MSDN :

Методите get и set обикновено не се различават от другите методи. Те могат да изпълняват всяка програмна логика, да хвърлят изключения, да бъдат отменяни и да бъдат декларирани с всякакви модификатори, разрешени от езика за програмиране. Имайте предвид обаче, че свойствата могат да бъдат и статични. Ако дадено свойство е статично, има ограничения за това какво могат да правят методите get и set. Вижте вашия език за програмиране за подробности.


person dlras2    schedule 25.05.2010    source източник
comment
Прочетох този въпрос, отговорите и дори го свързах в собствения си въпрос. Не видях нищо за повдигане на събития, което според мен заслужава различни отговори.   -  person dlras2    schedule 26.05.2010
comment
@Rowland: Мисля, че е достатъчно специфично, за да бъде различно.   -  person Steven Sudit    schedule 26.05.2010
comment
Кой гласува това да бъде мигрирано към Super User?   -  person ChrisF    schedule 26.05.2010
comment
То е свързано с редица други въпроси, но не е ТОЧЕН дубликат, тъй като обхваща нова тема.   -  person Steven Sudit    schedule 26.05.2010
comment
Броят други въпроси, които намерих, също ги свързах, така че хората да могат да видят информацията, която не сметнах за решаваща, вместо просто да ме свързват с въпроси, които вече бях прочел, или затваряне това като дубликат. Явно просто им улесних работата.   -  person dlras2    schedule 26.05.2010


Отговори (3)


Моето виждане:

  1. Ако сетер или гетер се очаква да бъде скъп, не го превръщайте в свойство, направете го метод.

  2. Ако настройката на свойство задейства събития поради промени, това е добре. Как иначе бихте позволили на слушателите да бъдат уведомявани за промени? Въпреки това може да искате да предложите двойка BeginInit/EndInit за потискане на събития, докато не бъдат направени всички промени. Обикновено отговорността на манипулатора на събитието е да се върне незабавно, но ако наистина не можете да му се доверите, че ще го направи, тогава може да поискате да сигнализирате събитието в друга нишка.

  3. Ако настройката на свойство хвърля изключения за невалидни стойности, това също е добре. Това е разумен начин да сигнализирате за проблема, когато стойността е напълно грешна. В други случаи задавате куп свойства и след това извиквате метод, който ги използва, за да направи нещо, като например създаване на връзка. Това би позволило задържане на валидиране и обработка на грешки, докато свойствата не бъдат използвани, така че свойствата няма да трябва да хвърлят нищо.

  4. Достъпът до собственост може да има странични ефекти, стига да не са неочаквани и да нямат значение. Това означава, че JIT екземпляр в getter е добре. По същия начин, задаването на мръсен флаг за екземпляра, когато се прави промяна, е съвсем добре, тъй като задава свързано свойство, като например различен формат за същата стойност.

  5. Ако прави нещо вместо просто достъп до стойност, това трябва да е метод. Методите са глаголи, така че създаването на връзка ще се извърши от метода OpenConnection(), а не от свойство Connection. Свойство Connection ще се използва за извличане на използваната връзка или за свързване на екземпляра към друга връзка.

редактиране - добавено 5, променено 2 и 3

person Steven Sudit    schedule 25.05.2010

Съгласен съм с идеята, че getters/settings не трябва да имат странични ефекти, но бих казал, че те не трябва да имат неочевидни странични ефекти.

Що се отнася до хвърлянето на изключения, ако задавате свойство на невалидна стойност (в много основен смисъл), тогава изключенията за валидиране са добре. Въпреки това, ако сетерът изпълнява цяла поредица от сложни валидации на бизнес правила или се опитва да излезе и актуализира други обекти или каквото и да е друго нещо, което може да причини изключение, тогава това е лошо. Но този проблем всъщност не е проблем със самото изключение, а по-скоро, че сетерът се изключва и тайно изпълнява много функции, които повикващият не би (или не трябва) да очаква.

Същото и със събитията. Ако сетер хвърля събитие, което казва, че "това свойство се е променило", тогава е ОК, защото това е очевиден страничен ефект. Но ако задейства някакво друго персонализирано събитие, така че да накара някаква скрита част от кода да се изпълни в друга част на системата, това е лошо.

Това е същата причина, поради която избягвам мързеливото зареждане в гетъри. Наистина, те могат да направят нещата по-лесни през много време, но могат да направят нещата по-объркващи през някои от времето, защото винаги има сложна логика около това кога точно искате дъщерните обекти да бъдат заредени. Обикновено това е само още един ред код за изрично зареждане на дъщерните обекти, когато попълвате родителския обект, и това може да избегне много объркване относно състоянието на обекта. Но този аспект може да стане много субективен и до голяма степен зависи от ситуацията.

person Mike Mooney    schedule 25.05.2010
comment
Избягването на мързел може да има значителни разходи за производителност, така че не мисля, че това е добра идея като цяло. - person Steven Sudit; 26.05.2010
comment
@Steven Sundit: Да, това е компромис и зависи от ситуацията. Ако предварителното зареждане води до значителни разходи за производителност, тогава преследвайте отложено зареждане или по-ситуационно-специфично зареждане. Но често (и според моя опит обикновено) това не е така. - person Mike Mooney; 26.05.2010
comment
Предварителното зареждане със сигурност може да бъде подходящ избор, особено когато е евтино или еднократно. Когато не е нито едно от двете, тогава JIT става принудителен ход. - person Steven Sudit; 26.05.2010

Винаги съм намирал консервативния подход за най-добър, когато работя в C#. Тъй като свойствата са синтактично същите като полетата, те трябва да работят като полета: без изключения, без валидиране, без забавна работа. (Наистина, повечето от моите свойства започват като прости полета и не се превръщат в свойства, докато не е абсолютно необходимо.) Идеята е, че ако видите нещо, което изглежда като получаване или настройка на набор от полета, тогава това Е нещо като получаване или настройка на поле, по отношение на функционалност (няма хвърлено изключение), цялостна ефективност (настройването на променливи не задейства каскада от делегирани повиквания, например) и ефект върху състоянието на програмата (настройването на променлива задава тази променлива и не не призовавам много делегати, които биха могли да направят почти всичко).

Разумните неща за набор от свойства включват задаване на флаг, който да показва, че е имало промяна:

set {
    if(this.value!=value) {
        this.changed=true;
        this.value=value;
    }
}

Може би действително задайте стойност на друг обект, напр.:

set { this.otherObject.value=value; }

Може би разглобете входа малко, за да опростите вътрешния код на класа:

set {
    this.isValid=(value&Flags.IsValid)!=0;
    this.setting=value&Flags.SettingMask;
}

(Разбира се, в последните два случая функцията get може да направи обратното.)

Ако трябва да се случи нещо по-сложно, по-специално извикване на делегати, или извършване на проверка, или хвърляне на изключения, тогава моето мнение е, че дадена функция е по-добра. (Доста често моите полета се превръщат в свойства с get и set и след това завършват като свойство get и функция set.) По същия начин за гетерите; ако връщате препратка към нещо, това не е проблем, но ако създавате изцяло нов голям обект и го попълвате всеки път, когато свойството се чете - не е толкова горещо.

person Community    schedule 25.05.2010
comment
Ако работите в C#, тогава трябва да сте наясно с WPF, който зависи изцяло от промените в свойствата, като страничен ефект е да сигнализира за тези промени. Не виждам как можем да последваме съвета ви, без да осакатим нашия код. - person Steven Sudit; 26.05.2010
comment
Когато казвате страничния ефект от сигнализирането на тези промени, имате предвид повдигане на събитие? Или искаш да кажеш, че if(this.value!=value) return; би било лоша идея? Не съм работил с WPF. (Въпреки че програмирам на C#.) - person dlras2; 26.05.2010
comment
@Cyclotis: Е, това се отнася за повече от WPF, но WPF прави особено очевидно използване на известието за промяна на собствеността. Също така ще намерите примери на много места, където се използва обвързване на данни. Ако не ни е позволено да задействаме събития, когато има промяна, какво имаме вместо това? Проучване? - person Steven Sudit; 26.05.2010
comment
Все още съм леко объркан - C# не произвежда автоматично известия за промяна на свойството, нали? Все пак ще трябва да повдигате събития ръчно, нали? Имам следното в повечето от моите сетери: if (State == value) return; _state = value; if (StateChanged != null) OnStateChanged(EventArgs.Empty); - person dlras2; 26.05.2010
comment
@Cyclotis: Не, не е автоматично, но е задължително за определени видове обвързване. Вижте msdn.microsoft.com/en-us/library/ms743695.aspx - person Steven Sudit; 26.05.2010
comment
Не използвам WPF, само WinForms. (Колкото и да е странно, аз също съм голям фен на анкетите, а не на събитията...) Ако WPF ви принуди да направите нещо, вие сте блокирани и просто ще трябва да го направите, независимо какво мисля :) - person ; 26.05.2010
comment
Не е само WPF, но WPF е идеален случай. Разгледайте обвързването на данни в ASP.NET или WinForms и ще намерите различни събития за уведомяване, които се задействат от промени в свойствата. Що се отнася до анкетирането, то не е мащабируемо решение, особено когато става въпрос за многоядрени системи. - person Steven Sudit; 26.05.2010