[C#.NET] 應該避免屬性回傳類別,以免類別方法破壞內部結構(續)

  • 8521
  • 0
  • 2013-07-05

[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;
}

 

執行結果如下,可以看到資料真的被竄改了

image

 

我不死心的將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;
}

 

執行結果如下:

image

 


假設我有以下兩個類別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屬性也沒辦法對它進行方法操作

image

 


後記:

雖然說這樣做提高了安全性,但也降低了執行效能,若可以的話應該盡量用 ReadOnlyCollection<T>,但對.NET似乎沒有針對Dictionary<TKey,TValue>進行唯讀的方法,不過山不轉路轉已經有人實做出ReadOnlyDictionary<TKey,TValue>,請參考。

http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo