Hỏi về get set trong c#

Chào mọi người. Cho em xin hỏi về sự khác nhau giữa 2 cách khai báo như nhau trong c#:

public class myClass
{
//cái này
public int myInt;
//với cái này
public int myInt1 {get; set;}
}

Em thấy cả 2 rất giống nhau đều có thể lấy hoặc gán dữ liệu vào biến. Vậy cho em hỏi là nó thực sự khác nhau chỗ nào ạ.

2 Likes

Nó là bao đóng. Tương tự getter và setter của java.

Anh có thể nói rõ hơn được không?
Bao đóng là gì?
Khai báo như thế nào là tốt nhất?
Vậy khai báo như vậy nó có thực sự khác biệt gì không a?
(em newbie mong anh có thể chỉ thêm)

2 Likes

Vậy thì bạn nên đọc lại lý thuyết lập trình hướng đối tượng nhé. Giải thích rất dài dòng và trong 1 vài comment ko thể trả lời hết được

Vậy thì cho e hỏi là khai báo vậy nó khác nhau chỗ nào ạ:

public class myClass
{
//cái này
public int myInt1 {get; set;}
//với cái này
private int myint2;
public int MYINT2
            {
                get { return myint2; }
                set { myint2 = value; }
            }
}
1 Like

Không khác nhau gì cả.
Chỉ khác nhau trong 1 số trường hợp cụ thể, ví dụ khi ở trong hàm set, bạn muốn kiểm tra giá trị được set rồi mới gán giá trị.

set { if(value >0 ) myint2 = value; }

Vâng. Em cám ơn nhé :slight_smile:

public int myInt1{get;set;}

Là cách viết ngắn gọn của :

int temp;
public int myInt1{
    get{ return temp;}
    set{temp=value;}
}
2 Likes

public int myInt1 {get; set;} thì myInt1 gọi là property. Property myInt1 có thể xem là 2 hàm: getter và setter. Compiler sẽ tự động tạo:

  • biến (tên ví dụ) private int _myInt1
  • hàm int get_myInt1() { return _myInt1; }
  • hàm int set_myInt1(int value) { _myInt1 = value; return _myInt1; }

thay vì lấy giá trị của _myInt1 bằng cách viết obj.get_myInt1() thì với property có thể viết ngắn gọn là obj.myInt1.
thay vì gán giá trị cho _myInt1 bằng cách viết obj.set_myInt1(10) thì với property có thể viết ngắn gọn là obj.myInt1 = 10.

nếu chỉ viết {get; set;} ko thì chả khác gì khai báo public int myInt1; cả. Công dụng của property được áp dụng ở chỗ khác. Ví dụ 1 class Character có private int hp. Thay vì phải viết hàm void addHealth(int amount) thì có thể viết 1 property setter cho hp:

public int HP
{
    get { return hp; }
    set
    {
        hp = value;
        if (hp > MAX_HP) hp = MAX_HP; //tránh hp ko vượt quá MAX_HP
    }
}

như vậy có thể viết character.HP = character.HP + 20; //tăng 20 máu mà vẫn bảo đảm logic là hp của nhân vật ko vượt quá max hp ví dụ là 100. Đơn giản hơn là gọi character.addHealth(20); vì phải nhớ phương thức addHealth :joy: Mặc dù HP ko phải là 1 thuộc tính (attribute) của class Character nhưng ở đây ta có thể xem nó y như là thuộc tính hp vậy. Ngoài ra gọi character.HP gọn gàng hơn gọi character.getHp() nhiều.

12 Likes

chuẩn rồi.hehe.c# dùng property để đóng gói với tự nhiên hơn

Mình ko thực sự hiểu cách viết
public int myInt1{get;set;}
Nó có ý nghĩa gì trong vấn đề đóng gói
Nếu chỉ viết {get;set;} như thế tại sao ta ko viết
public int myInt1;

1 Like

public int myInt1{get;set;}
Cách viết này được gọi là auto-properties (tự động tạo thuộc tính),
nó tương đương với

private int _myInt1
int getMyInt1() { return _myInt1; }
hàm int setMyInt1(int value) { _myInt1 = value; return _myInt1; }

Còn tại sao ta không dùng
public int myInt1;
mặc dù nó tương đương?

Nó liên quan tới khái niệm Encapsulation (đóng gói trong OOP)
Với cách1: tuy bạn gọi obj.myInt1=2, nhưng thực ra nó sẽ gọi obj.setMyInt1(2)
Với cách 2: bạn gọi obj.myInt1=2 là set trực tiếp giá trị myInt1 thành 2.

Vậy đóng gói có tác dụng gì?

  • Che giấu thông tin thuộc tính (chỉ expose các API giúp thao tác các thuộc tính): Nếu bạn dùng các tool debug (khái niệm Reflection) thì bạn không thể xem thuộc tính của obj được. Nó giúp quá trình bảo mật thông tin (vì hoàn toàn bạn không cần khai báo hàm get). Nó giống như trường hợp, every thằng bạn của bạn được quyền truy cập tài khoản của bạn để biết bạn giầu thế nào? và bạn expose ra một hàm (tức là nó chỉ được quyền hỏi mầy giàu thế nào, còn trả lời sao thì là quyền của mình)
  • Đóng gói còn giúp mình kiểm tra dữ liệu (validation) đưa vào. Cũng ví dụ trên, giờ thằng bạn mà truy cập tài khoản tiền trực tiếp, nhỡ nó đưa tiền bẩn, tiền mafia vào thì sao? Mình ở hàm setMyInt1, kiểm tra xem tiền này có sạch không? Sạch thì cho vào, không sạch thì throw exception chẳng hạn.

:grinning:đọc lại OOP về Encapsulation là hiểu thôi mà (capsule là con nhộng, viên thuốc ý: bạn có nhìn thấy thành phần gì bên trong không? Đóng gói tức là bạn chỉ biết phía bên ngoài vậy thôi, đọc hướng dẫn sử dụng mà dùng (API) chứ không ai chọc nó ra xem thành phần thuốc rồi dùng cả ^^)

8 Likes

Cũng có khá nhiều tranh luận có nên đóng gói hay không đóng gói. Rõ ràng với code không đóng gói, sẽ nhanh hơn, dễ hiểu hơn, trong sáng hơn, nhất là debug thì public nó show hàng lồ lộ ra, thích hơn hẳn ^^, rồi serialize/deserialize nữa, dễ hơn rất nhiều.

Một số trường hợp cần đóng gói như:

  • Cần thêm logic (validation) cho thuộc tính (khỏi code nhé, lấy cái ví dụ acc kiểm tra tiền bẩn)
  • Lazy load thuộc tính
private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }
        return _password;
    }
}
  • Sử dụng DI (injection) để có thể sử dụng các GLOBAL variable dùng trong class, ví dụ như mình cần cái connection kết nối db dùng trong gateway.
private Connection _conn;
void setConnection(Connection conn) {_conn= conn}

mình inject chứ không khai báo

public Connection _conn = Blabla.getConnection();

Injection giúp trong việc làm TDD, vì mình hoàn toàn có thể cho một conn mock (là một conn luôn đúng), chứ nếu cho conn thật, nhỡ nó fail thì cả cái class của mình fail hết à.

2 Likes

Khi design chỉ cần nhớ nguyên tắc KISS keep it simple and small, là được. Nếu chỉ có bạn dùng class đó, dùng public cũng không sao cả, vừa nhanh, vừa đỡ tốn bộ nhớ.

Ngay cả việc viết auto-property cũng giúp việc đóng gói KISS đó (viết có một dòng, thay vì bao nhiêu dòng lê ta thê).

Mình có câu hỏi, vậy public int myInt1{get,set} là property hay method? ^^

1 Like

hình như KISS là “keep it simple, stupid” mà :joy:

1 Like

Ừ, chuẩn là stupid, nhưng dùng stupid trong lập trình nghe hơi … dummy quá. Dùng biến thể của nó cho hợp lý hơn ^^

2 Likes

rõ ràng là auto property get set vi phạm quy tắt đóng gói. Nếu chỉ viết {get; set;} “về cách sử dụng” thì chả khác gì khai báo biến public cả, nên hạn chế hoặc ko viết như vậy. Nên viết thêm kiểm tra điều kiện cho getter setter nữa.

http://blog.codinghorror.com/properties-vs-public-variables/

chắc là boss này trả lời đầy đủ vấn đề property :joy:

2 Likes

Nếu chỉ có get, set thì về ý nghĩa sử dụng đa phần không khác gì biến public. Nhưng đó là cách viết của 1 property hết sức đơn sơ và bình thường và thực tế thì nó sẽ không chỉ có get, set. Khi đó bạn mới thấy khác biệt với biến public.
Ví dụ 3 cách thế này bạn sẽ thấy nó khác, bạn thử sử dụng xem nó khác thế nào.


public int MyInt{get; private set;}
public int MyInt{private get; set;}

private int _myint=0;
public int MyInt{
    get{ return _myint;}
    set{
         if(value<0) _myint=0;
         else if(value>100) _myint=100;
         else _myint=value;
    }
}
3 Likes

3 cách dùng bên dưới thì rõ ràng là có sự khác biệt. Nhưng nếu chỉ {get;set;} mình thấy không có ý nghĩa gì cả (tất nhiên về bản chất 1 cái là biến, 1 cái là property). Ko biết mình có hiểu sai không nhưng như thế giống như người code đang dùng cửa kính cho nhà tắm vậy, vẫn là đóng cửa nhưng mà :joy:

Nhưng mà không sờ được :smiling_imp:
Dù gì thì vẫn hơn là không có kính :stuck_out_tongue:

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?