[C#.NET] 應該避免屬性回傳類別,以免類別方法破壞內部結構(續)
續上篇[.NET] 應該避免屬性回傳類別,以免類別方法破壞內部結構
本以為回傳IEnumerable<int>已經夠安全了,沒想到…
public class Class1
{
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));
}
}
}
以為萬無一失的保護,沒想到被敵方識破,被猜出了我是用List<int>,轉型成功,竄改了結果,前線防線決堤..
private void button4_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
c.GetValue();
List<int> list = (List<int>)c.Datas;
list.Add(0);
foreach (var item in c.Datas)
{
Console.WriteLine(item.ToString());
}
this.comboBox1.DataSource = c.Datas;
}
執行結果如下,可以看到資料真的被竄改了
我不死心的將Datas屬性改寫如下,只要有人存取Datas屬性就會重新跟_Datas要資料,然後回傳一份假的副本,使用者再怎麼改資料都不會改到核心_Datas。
private List<int> _Datas = new List<int>();
public IEnumerable<int> Datas
{
get
{
List<int> list = new List<int>();
foreach (var item in this._Datas)
{
list.Add(item);
}
return list;
}
}
這樣一來,總該真的萬無一失了吧!!!就算讓敵方知道我們是用List<int>也不用怕了
private List<int> _Datas = new List<int>();
public List<int> Datas
{
get { return Clone(); }
}
List<int> Clone()
{
List<int> list = new List<int>();
foreach (var item in this._Datas)
{
list.Add(item);
}
return list;
}
這時候用戶端所改的list變數將不會影響到c.Datas,c.Datas資料筆數將維持5筆。
private void button4_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
c.GetValue();
List<int> list =c.Datas;
list.Add(0);
foreach (var item in c.Datas)
{
Console.WriteLine(item.ToString());
}
this.comboBox1.DataSource = c.Datas;
}
執行結果如下:
假設我有以下兩個類別Person、Name
public class Person
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
private List<string> _Phone = new List<string>();
public List<string> Phone
{
get { return this._Phone; }
set { this._Phone = value; }
}
}
public class Name
{
public string FristName { get; set; }
public string LastName { get; set; }
public Name(string frisName, string lastName)
{
this.FristName = frisName;
this.LastName = lastName;
}
}
然後在Families類別建立Person屬性,_Person.ToDictionary方法同樣是回傳一個副本。
public class Families
{
public Families()
{
Person p1 = new Person();
p1.Address = "王媽家";
p1.Age = 55;
p1.Name = new Name("王", "大月");
p1.Phone.Add("02-1234567890");
p1.Phone.Add("02-1111111111");
this._Person.Add("王媽家", p1);
}
private Dictionary<string, Person> _Person = new Dictionary<string, Person>();
public Dictionary<string, Person> Person
{
get
{
var dic = _Person.ToDictionary(person => person.Key, person => person.Value);
return dic;
}
}
}
所以這樣做在用戶端就算看到了Families.Person屬性也沒辦法對它進行方法操作
後記:
雖然說這樣做提高了安全性,但也降低了執行效能,若可以的話應該盡量用 ReadOnlyCollection<T>,但對.NET似乎沒有針對Dictionary<TKey,TValue>進行唯讀的方法,不過山不轉路轉已經有人實做出ReadOnlyDictionary<TKey,TValue>,請參考。
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET