Get set trong c# là gì
Nếu bạn học qua OOP bạn sẽ học tới phần đóng gói, che dấu dữ liệu phía sau, không cho truy cập trực tiếp mà phải thông qua một thằng cò trung gian. Ví dụ bạn muốn lấy tiền, bạn không thể mở tủ tiền ra lấy trực tiếp được, mà phải thông qua bố hoặc mẹ bạn. Khi đó Getter nói chung làm nhiệm vụ như mẹ bạn có quyền xử lý số tiền đó trước khi đưa cho bạn. Setter tương tự. Phía trên Setter hay Getter mới chỉ là định nghĩa. Khi ta thực hiện nó sẽ trông như sau. Với Java bạn thường thông qua một method get...() hay set...() mà bên trong xử lý sao đó mà get trả về kiểu của data cần lấy ra, còn set thường sẽ trả về void. public Money getMoney() { // Đặt xử lý của bạn chỗ này return this._money; } public void setMoney(Money value) { // Đặt xử lý của bạn chỗ này this._money = value; }Với C# thì nó rút gọn lại thành mấy cái keyword get set cho nó tiện hơn thôi. Còn lại bản chất nó như một method. Chào anh chị ạ, Em đang học C# trên website và học đến phần C# OOP. Bản thân em tự dưng có một thắc mắc khi em học đến Class. Em giả sử em có một đoạn code sau ạ: class SinhVien { private double diemTB; public double DiemTB { get { return double; } set { diemTB = value; } } }Em để ý ở trường học của em cũng sử dụng tương tự HowKTeam, nhưng em không hiểu tại sao phải dùng private chi cho mắc công, tốn sức rồi lại sử dụng thêm get, set, trong khi đó mình có thể dùng trực tiếp public để truy cập cho dễ cũng được mà. class SinhVien { public double diemTB; }Em có hỏi thì một số bạn cũng có trả lời em là do bảo mật không cho truy cập gì ấy nhưng em vẫn chưa hình dung được. Rất mong anh chị giải đáp, em xin cảm ơn ạ. Ngày làm việc của mình thường bắt đầu bằng việc mở một project nào đó, ngắm nghía và review chúng. Hôm nay chợt mình nhìn thấy đoạn code get set này làm mình nhớ tới câu chuyện ngày trước: private int Month= 1; public int Month { get {return _Month} set { if (value>=1 && value <=12) _Month= value; else _Month =1; } } Đại khái đây là phần định nghĩa hàm
trả lại string. Đoạn code này làm mình nhớ tới lần đầu vọc code, không hiểu được get với set để làm gì, tại sao không khai báo luôn trong hàm đi? Mình đoán rất nhiều bạn cũng sẽ có câu hỏi như mình. Và đây là ý kiến riêng của mình: – Get và set đơn hiểu đơn giản là để quy định miền giá trị, phạm vi của biến. Như ở đoạn code phía trên, biến Month đươc set bằng 1 nếu 1<=value<=12 và get lấy giá trị của _Month. – Tại sao lại phải khai báo get, set mà không khai báo trực tiếp. Lý
do là : Với một bài tập nhỏ, một project nhỏ, thì nhìn chung hai cách trên không khác nhau, nhưng nếu bạn muốn làm một project lớn hơn, có thể dùng lại thì get set thể hiện được sự ưu việt của mình: bạn muốn gọi tới một biến nhưng nó lại được khai báo private để bảo mật, do đó ta cần một phương thức, đó chính là get, set. Get, set trong lập trình là một trong những căn bản mà bạn nên biết và hiểu cặn kẽ, nếu bạn muốn bước đi trên con đường của một lập trình viên.
Related postsBan đầu tôi đến từ thế giới C # và tôi đang học C ++. Tôi đã tự hỏi về các hàm get và set trong C ++. Trong C #, việc sử dụng chúng khá phổ biến và các công cụ như Visual Studio thúc đẩy việc sử dụng bằng cách thực hiện chúng rất dễ dàng và nhanh chóng. Tuy nhiên, điều này dường như không xảy ra trong thế giới C ++. Đây là mã C # 2.0: public class Foo { private string bar; public string Bar { get { return bar; } set { bar = value; } } }Hoặc, trong C # 3.0: public class Foo { get; set; }Có thể mọi người sẽ nói, điều đó có ý nghĩa gì? Tại sao không chỉ tạo một trường công khai và sau đó biến nó thành một thuộc tính sau này nếu bạn cần; thành thật mà nói, tôi thực sự không chắc chắn. Tôi chỉ làm điều đó vì thực hành tốt vì tôi đã thấy nó làm rất nhiều lần. Bây giờ vì tôi đã quá quen với việc này, tôi cảm thấy mình nên chuyển thói quen sang mã C ++ của mình, nhưng điều này có thực sự cần thiết không? Tôi không thấy nó được thực hiện thường xuyên như với C #. Dù sao, đây là C ++ từ những gì tôi thu thập được: class Foo { public: std::string GetBar() const; // Thanks for the tip, @Daniel Earwicker. void SetBar(std::string bar); private: std::string bar; } std::string Foo::GetBar() const { return bar; } void Foo::SetBar(std::string bar) { // Also, I always wonder if using 'this->' is good practice. this->bar = bar; }Bây giờ, đối với tôi, đó có vẻ như là một công việc chân tay; xem xét việc sử dụng các công cụ của Visual Studio, việc triển khai C # sẽ mất vài giây để thực hiện và C ++ khiến tôi mất nhiều thời gian hơn để nhập - tôi cảm thấy nó không đáng để nỗ lực, đặc biệt là khi phương án thay thế dài 5 dòng: class Foo { public: std::string Bar; }Từ những gì tôi thu thập được, đây là những lợi thế:
Và những nhược điểm:
Câu trả lời:Tại sao tôi lại chọn câu trả lời với số phiếu ít hơn ? Tôi thực sự đã rất gần với việc chọn câu trả lời của veefu ; tuy nhiên, ý kiến cá nhân của tôi (mà rõ ràng là gây tranh cãi), đó là câu trả lời nằm trên bánh pudding. Mặt khác, câu trả lời tôi đã chọn dường như tranh luận cả hai bên; Tôi nghĩ rằng getters và setters là xấu nếu được sử dụng quá mức (ý tôi là, khi nó không cần thiết và sẽ phá vỡ mô hình kinh doanh), nhưng tại sao chúng ta không nên có một hàm được gọi GetBalance()? Chắc chắn điều này sẽ linh hoạt hơn nhiều so với PrintBalance(); điều gì sẽ xảy ra nếu tôi muốn hiển thị nó cho người dùng theo một cách khác chứ không phải như lớp tôi muốn? Bây giờ, theo một nghĩa nào đó, GetBalance()có thể không đủ liên quan để lập luận rằng "getters và setters là tốt" bởi vì nó không (hoặc có thể, không nên ) có một setter đi kèm và nói về điều này, một hàm được gọi là SetBalance(float f)có thể không tốt ( theo ý kiến của tôi) bởi vì nó sẽ ngụ ý đối với người triển khai chức năng rằng tài khoản phải được thao tác bên ngoài lớp, đó không phải là một điều tốt.
41 hữu ích 3 bình luận 92k xem chia sẻ answer 44 Tôi cho rằng việc cung cấp trình truy cập quan trọng hơn trong C ++ so với C #. C ++ không có hỗ trợ nội trang cho các thuộc tính. Trong C #, bạn có thể thay đổi trường công cộng thành thuộc tính mà không cần thay đổi mã người dùng. Trong C ++ điều này khó hơn . Đối với việc nhập ít hơn, bạn có thể triển khai các setters / getters tầm thường như các phương thức nội tuyến: class Foo { public: const std::string& bar() const { return _bar; } void bar(const std::string& bar) { _bar = bar; } private: std::string _bar; };Và đừng quên rằng getters và setters hơi ác. 44 hữu ích 2 bình luận chia sẻ answer 36 Trước nguy cơ gây tranh cãi, tôi sẽ quay lại một quan điểm đối lập mà tôi gặp phải lần đầu khi đọc "Holub on Patterns". Đó là một quan điểm rất thách thức, nhưng có ý nghĩa đối với tôi khi suy ngẫm: Getters and Setters is Evil Việc sử dụng getters và setters đối lập với các nguyên tắc cơ bản của thiết kế hướng đối tượng: Trừu tượng hóa và đóng gói dữ liệu. Việc sử dụng quá nhiều getters và setters sẽ làm cho mã của bạn kém linh hoạt và khó bảo trì về lâu dài. Cuối cùng, chúng tiết lộ việc triển khai cơ bản của lớp của bạn, khóa các chi tiết triển khai vào giao diện của lớp. Hãy tưởng tượng trường 'std :: string Foo :: bar' của bạn cần thay đổi từ một chuỗi std :: sang một lớp chuỗi khác, chẳng hạn như được tối ưu hóa tốt hơn hoặc hỗ trợ một bộ ký tự khác. Bạn sẽ cần thay đổi trường dữ liệu riêng tư, getter, setter và tất cả mã máy khách của lớp này gọi những getters và setters này. Thay vì thiết kế các lớp của bạn để "cung cấp dữ liệu" và "nhận dữ liệu", hãy thiết kế chúng để "thực hiện các hoạt động" hoặc "cung cấp dịch vụ". Hãy tự hỏi tại sao bạn lại viết hàm "GetBar". Bạn đang làm gì với dữ liệu đó? Có lẽ bạn đang hiển thị dữ liệu đó hoặc thực hiện một số xử lý trên đó. Quá trình này có được tiếp xúc tốt hơn như một phương pháp của Foo không? Điều này không có nghĩa là getters và setters không có mục đích của chúng. Trong C #, tôi tin rằng lý do cơ bản để sử dụng chúng là giao diện với IDE thiết kế GUI của Visual Studio, nhưng nếu bạn thấy mình đang viết chúng bằng C ++, có lẽ tốt nhất bạn nên lùi lại một bước, xem lại thiết kế của mình và xem có điều gì không bị mất tích. Tôi sẽ cố gắng mô phỏng một ví dụ để minh họa. // A class that represents a user's bank account class Account { private: int balance_; // in cents, lets say public: const int& GetBalance() { return balance_; } void SetBalance(int b) { balance_ = b; } }; class Deposit { private: int ammount_; public: const int& GetAmount() { return ammount_; } void SetAmmount(int a) { _balance = a; } }; void DoStuffWithAccount () { Account a; // print account balance int balance = a.GetBalance(); std::cout << balance; // deposit some money into account Deposit d(10000); a.SetBalance( a.GetBalance() + d.GetValue()); }Không mất nhiều thời gian để thấy rằng nó được thiết kế rất kém.
Các getters và setters làm cho việc khắc phục sự cố trở nên khó khăn hơn, vì mã khách hàng DoStuffWithAccount hiện bị ràng buộc với kiểu dữ liệu mà chúng tôi đã sử dụng để triển khai số dư tài khoản. Vì vậy, hãy chuyển mã này và xem chúng tôi có thể cải thiện những gì // A class that represents a user's bank account class Account { private: float balance_; public: void Deposit(float b) { balance_ += b; } void Withdraw(float w) { balance_ -= w; } void DisplayDeposit(std::ostream &o) { o << balance_; } }; void DoStuffWithAccount () { Account a; // print account balance a.DisplayBalance(std::cout); // deposit some money into account float depositAmt = 1000.00; a.Deposit(depositAmt); a.DisplayBalance(std::cout); }'Phao' là một bước đi đúng hướng. Được cấp, bạn có thể đã thay đổi loại nội bộ thành 'float' và vẫn hỗ trợ thành ngữ getter / setter: class Account { private: // int balance_; // old implementation float balance_; public: // support the old interface const int& GetBalance() { return (int) balance_; } void SetBalance(int b) { balance_ = b; } // provide a new interface for the float type const float& GetBalance() { return balance_; } // not legal! how to expose getter for float as well as int?? void SetBalance(float b) { balance_ = b; } };nhưng không mất nhiều thời gian để nhận ra rằng sự sắp xếp getter / setter đang tăng gấp đôi khối lượng công việc của bạn và làm phức tạp thêm vấn đề vì bạn cần hỗ trợ cả mã đã sử dụng int và mã mới sẽ sử dụng float. Chức năng Gửi tiền giúp việc mở rộng phạm vi các loại hình thức gửi tiền dễ dàng hơn một chút. Một lớp giống như Tài khoản có lẽ không phải là ví dụ tốt nhất, vì "lấy" số dư tài khoản là một hoạt động tự nhiên đối với một Tài khoản. Tuy nhiên, điểm tổng thể là bạn phải cẩn thận với các bộ chuyển đổi và định vị. Đừng có thói quen viết getters và setters cho mọi thành viên dữ liệu. Nó khá dễ dàng để lộ và tự nhốt mình vào một triển khai nếu bạn không cẩn thận. 36 hữu ích 5 bình luận chia sẻ answer 11 Trong ví dụ của bạn: class Foo { public: const std::string GetBar(); // Should this be const, not sure?Có thể bạn muốn nói điều này: std::string GetBar() const;Đặt constở cuối có nghĩa là "Hàm này không sửa đổi cá thể Foo mà nó được gọi", vì vậy theo một cách nào đó, nó đánh dấu nó là một getter thuần túy. Getters thuần túy xảy ra thường xuyên trong C ++. Một ví dụ trong std::ostringstreamlà str()hàm. Thư viện Chuẩn thường tuân theo một mô hình sử dụng cùng một tên hàm cho một cặp hàm getter / setter - strlại là một ví dụ. Về việc liệu có quá nhiều việc để đánh máy hay không và nó có đáng không - đó có vẻ là một câu hỏi kỳ quặc! Nếu bạn cần cung cấp cho khách hàng quyền truy cập vào một số thông tin, hãy cung cấp một getter. Nếu bạn không, thì đừng. 11 hữu ích 5 bình luận chia sẻ answer 6 Không có quy ước thực sự nghiêm ngặt về điều này, giống như trong C # hoặc Java. Nhiều lập trình viên C ++ sẽ chỉ công khai biến để tránh rắc rối cho chính họ. Như các câu trả lời khác đã nói, bạn thường không cần thiết lập, và ở một mức độ nào đó, hãy nhận các phương pháp. Nhưng nếu và khi bạn tạo chúng, bạn không cần phải nhập nhiều hơn mức cần thiết: class Foo { public: std::string Bar() const { return bar; } void Bar(const std::string& bar) { this->bar = bar; } private: std::string bar; };Khai báo nội tuyến các hàm trong lớp giúp lưu việc nhập và gợi ý cho trình biên dịch mà bạn muốn các hàm được nội dòng. Và nó không đánh máy nhiều hơn so với các ứng dụng C # tương đương. Một điều cần lưu ý là tôi đã loại bỏ các tiền tố get / set. Thay vào đó, chúng ta chỉ có hai quá tải Bar (). Điều đó khá phổ biến trong C ++ (xét cho cùng, nếu nó không có bất kỳ đối số nào, chúng tôi biết đó là getter, và nếu nó có đối số, nó là setter. Chúng tôi không cần tên để cho chúng tôi biết điều đó), và nó tiết kiệm hơn một chút đánh máy. 6 hữu ích 2 bình luận chia sẻ answer 6 [sửa] Có vẻ như tôi cần phải nhấn mạnh rằng các bộ cài đặt cần xác thực các tham số và thực thi các tham số bất biến, vì vậy chúng thường không đơn giản như ở đây. [/biên tập] Không phải với tất cả, bởi vì đánh máy thêm. Bây giờ tôi có xu hướng sử dụng chúng thường xuyên hơn vì Visual Assist cung cấp cho tôi "trường đóng gói". Các công việc sẽ không nhiều hơn nếu bạn chỉ triển khai các setters / getters nội tuyến mặc định trong khai báo lớp (điều mà tôi có xu hướng làm - mặc dù vậy, các setters phức tạp hơn sẽ di chuyển đến phần thân). Một số lưu ý: constness: Có, getter phải là const. Tuy nhiên, nó không được sử dụng để làm cho giá trị trả về là const, nếu bạn trả về theo giá trị. Đối với các giá trị trả về có khả năng phức tạp, bạn có thể muốn sử dụng const & mặc dù: Chuỗi setter : Nhiều nhà phát triển muốn sửa đổi setter như vậy: Foo & SetBar(std::string const & bar) { this->bar = bar; return *this; }Điều này cho phép gọi nhiều bộ cài đặt như vậy: Foo foo; foo.SetBar("Hello").SetBaz("world!");Tuy nhiên, nó không được chấp nhận rộng rãi như một điều tốt. __declspec (thuộc tính) : Visual C ++ cung cấp phần mở rộng không chuẩn này để người gọi có thể sử dụng lại cú pháp thuộc tính. Điều này làm tăng tính hợp pháp trong lớp một chút, nhưng làm cho mã người gọi trông thân thiện hơn nhiều. Vì vậy, kết luận, có một chút công việc khó khăn hơn, nhưng một số ít các quyết định cần thực hiện trong C ++. Điển hình ;) 6 hữu ích 4 bình luận chia sẻ answer 4 Tôi hầu như không bao giờ sử dụng getters và setters trong mã của riêng tôi. Câu trả lời của Veefu có vẻ tốt với tôi. Nếu bạn khăng khăng muốn có getters và / hoặc setters, bạn có thể sử dụng macro để cắt bớt tấm nồi hơi. #define GETTER(T,member) const T& Get##member() const { return member; } #define SETTER(T,member) void Set##member(const T & value) { member = value; } class Foo { public: GETTER(std::string, bar) SETTER(std::string, bar) private: std::string bar; }4 hữu ích 0 bình luận chia sẻ answer 3 Lấy và thiết lập các thành viên dữ liệu qua các thành viên dữ liệu: Xấu . 3 hữu ích 0 bình luận chia sẻ answer 3 Các lập luận chống lại Get / Set về mặt thiết kế API trong ví dụ ngân hàng được đưa ra. Không để lộ các trường hoặc thuộc tính nếu chúng cho phép người dùng vi phạm các quy tắc kinh doanh của bạn. Tuy nhiên, khi bạn đã quyết định rằng bạn cần một trường hoặc thuộc tính, hãy luôn sử dụng thuộc tính. Các thuộc tính tự động trong c # rất dễ sử dụng và có nhiều trường hợp (liên kết dữ liệu, tuần tự hóa, v.v.) không hoạt động với các trường, nhưng yêu cầu thuộc tính. 3 hữu ích 0 bình luận chia sẻ answer 1 Nếu bạn đang phát triển các thành phần COM thì có, nó rất phổ biến. 1 hữu ích 0 bình luận chia sẻ answer 1 lấy và thiết lập là một nỗi đau gây ra cho mọi người nếu bạn phải sử dụng chúng trong bất kỳ ngôn ngữ nào. Eiffel làm tốt hơn rất nhiều ở chỗ tất cả những gì khác biệt là lượng thông tin bạn phải cung cấp để có câu trả lời - một hàm có 0 parms giống như việc truy cập vào một biến thành viên và bạn có thể thay đổi tùy ý giữa chúng. Khi bạn kiểm soát cả hai mặt của một giao diện, định nghĩa của giao diện dường như không phải là một vấn đề lớn như vậy. Tuy nhiên, khi bạn muốn thay đổi chi tiết triển khai và nó dẫn đến việc biên dịch lại mã máy khách như trường hợp phổ biến trong C ++, bạn muốn có thể giảm thiểu điều này càng nhiều càng tốt. Vì pImpl và get / set như vậy sẽ được sử dụng nhiều hơn trong các API công khai để tránh những thiệt hại như vậy. 1 hữu ích 0 bình luận chia sẻ answer 1 Phương thức Get và Set rất hữu ích nếu bạn có các ràng buộc trong một giá trị biến. Ví dụ, trong nhiều mô hình toán học, có một ràng buộc để giữ một biến float nhất định trong phạm vi [0,1]. Trong trường hợp này, Nhận và Đặt (Đặt đặc biệt) có thể đóng một vai trò tốt: class Foo{ public: float bar() const { return _bar; } void bar(const float& new_bar) { _bar = ((new_bar <= 1) && (new_bar >= 0))?new_bar:_bar; } // Keeps inside [0,1] private: float _bar; // must be in range [0,1] };Ngoài ra, một số thuộc tính phải được tính toán lại trước khi đọc. Trong những trường hợp đó, có thể mất rất nhiều thời gian tính toán không cần thiết để tính toán lại từng lớp biểu bì. Vì vậy, một cách để tối ưu hóa nó là chỉ tính toán lại khi đọc thay thế. Để làm như vậy, hãy nạp chồng phương thức Get để cập nhật biến trước khi đọc nó. Ngược lại, nếu không cần xác thực giá trị đầu vào hoặc cập nhật giá trị đầu ra, thì việc đặt thuộc tính công khai không phải là tội và bạn có thể thực hiện nó. 1 hữu ích 0 bình luận chia sẻ answer 0 Vâng, get và set rất phổ biến trong thế giới c ++. 0 hữu ích 5 bình luận chia sẻ answer 0 Nếu bạn sử dụng C ++ / CLI làm biến thể của C ++, thì nó có hỗ trợ thuộc tính bản địa trong ngôn ngữ, vì vậy bạn có thể sử dụng property String^ Name;Điều này cũng giống như String Name{get;set;}trong C #. Nếu bạn cần kiểm soát chính xác hơn các phương thức get / set thì bạn có thể sử dụng property String^ Name { String^ get(); void set(String^ newName); }trong tiêu đề và String^ ClassName::Name::get() { return m_name; } void ClassName::Name::set(String^ newName) { m_name = newName; }trong tệp .cpp. Tôi không thể nhớ rõ, nhưng tôi nghĩ bạn có thể có các quyền truy cập khác nhau cho các phương thức get và set (công khai / riêng tư, v.v.). Colin 0 hữu ích 0 bình luận chia sẻ answer -3 Trình biên dịch sẽ phát ra set_ và get_ nếu bạn xác định một thuộc tính, vì vậy nó thực sự chỉ cần lưu một số thao tác nhập. Đây là một cuộc thảo luận thú vị. Đây là một cái gì đó từ cuốn sách yêu thích của tôi "CLR qua C #". Đây là những gì tôi đã trích dẫn.
-3 hữu ích 1 bình luận chia sẻ |