[C#.NET] 應該避免屬性回傳類別,以免類別方法破壞內部結構
在上篇提到在設計一個類別時,我們應該使用屬性來處理取代全域變數,用來保護變數的存取權限,就像以下程式碼,我們可以確保它在類別裡回傳字串或其它實值型別,不會被用戶端修改,因為這些類別當中並沒有可以改變內部狀態的方法。
public class Class1
{
public string Address { get; private set; }
public string Name { get; private set; }
public int Age { get; private set; }
public void GetInfo()
{
this.Address = "高雄市";
this.Age = 20;
this.Name = "余小章";
}
}
什麼叫改變內部狀態?我們來看看以下程式碼,我設計一個GetValue方法,Datas屬性會得到GateValue方法的結果;以下程式碼看起來很好,我們原本是要設計了一個不開放修改的屬性,但是確忽略了回傳的類別裡的方法可以修改屬性內部結構,往往大部份的人都會忽略掉這一點,反而替用心良苦的設計的類別開了個後門。
類別程式碼
public class Class1
{
private List<int> _Datas = null;
public List<int> Datas
{
get
{
if (this._Datas == null)
this._Datas = new List<int>();
return this._Datas;
}
}
Random _random = new Random();
public void GetValue()
{
for (int i = 0; i < 5; i++)
{
this.Datas.Add(this._random.Next(1, 1000));
}
}
}
用戶端程式碼
static void Main(string[] args)
{
Class1 c = new Class1();
c.GetValue();
List<int> list = c.Datas;
c.Datas.Add(0);
c.Datas.ForEach((i) => Console.WriteLine(i.ToString()));
Console.ReadKey();
}
用心良苦關的門,卻忘了把拔掉鎖頭上的鑰匙,讓其他人闖了進來,c.Datas.Add(0)就是最好的見証。
我把程式碼改成如下,對於公開的Datas返回IEnumerable,而不是List<int>,以便實施資料繫結,當然不限定只能返回IEnumerable介面,在這裡剛好使用IEnumerable能解決問題。
private List<int> _Datas = new List<int>();
public IEnumerable<int> Datas
{
get { return this._Datas; }
}
Random _random = new Random();
public void GetValue()
{
for (int i = 0; i < 5; i++)
{
this._Datas.Add(this._random.Next(1, 1000));
}
}
用戶端程呼叫:
private void button2_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
c.GetValue();
var list = c.Datas;
foreach (var item in list)
{
Console.WriteLine(item.ToString());
}
this.comboBox1.DataSource = list;
}
或者使用 ReadOnlyCollection 來保護
private List<int> _Datas = new List<int>();
public ReadOnlyCollection<int> Datas
{
get
{
return new ReadOnlyCollection<int>(this._Datas);
}
}
Random _random = new Random();
public void GetValue()
{
for (int i = 0; i < 5; i++)
{
this._Datas.Add(this._random.Next(1, 1000));
}
}
以上兩個方法都沒公開類別的方法,用戶端自然就沒有辦法修改內部結構。
再舉個有問題的簡單例子,我有以下兩個類別Class1及Class2,Class2裡回傳了一個Class1的Data公開屬性
public class Class1
{
public string Name { get; internal set; }
public void GetName()
{
this.Name = "余小章 by Class1";
}
}
public class Class2
{
private Class1 _Data = new Class1();
public Class1 Data
{
get { return this._Data; }
}
public void SetName(string Name)
{
this._Data.Name = Name;
}
}
在用戶端的呼叫:
private void button3_Click(object sender, EventArgs e)
{
Class2 c2 = new Class2();
c2.SetName("Test");
c2.Data.GetName();//故意破壞內部結構
MessageBox.Show(c2.Data.Name);
}
原本只是想要公開c2.Data的屬性而已,卻沒注意到c2.Data的方法可以修改內部結構,事實上這樣的設計是有問題的,我們不應該公開在Class2中的Data屬性!
後記:
類別的資訊若要公開只希望用戶端查閱,應該要小心注意類別內有無改變結構的方法。
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET