C#中,struct與property的“愛恨糾葛”


       前言:我也是才開始從底層安全轉為應用軟件開發,由於因緣巧合開始從事C#開發,從零開始的學習過程中遇到一些非常有意思的現象,這里記錄下來,也一並把自己的理解寫下,如果對了,希望能給他人幫助;如果錯了,也希望大家諒解。


1. A property which is a struct.

       根據《effective c#》中的第一條:“Use Properties Instead of Accessible Data Members",反正就是多叫我們用property。這里遇到的問題是,如果一個類Program中,有個property叫做Prop,但是這個Prop封裝的其實是個struct類型的對象,那么會有什么有意思的東西呢?上代碼:
namespace test
{
struct MyStruct
{
public int i;
}

class Program
{
private MyStruct Prop { get; set; }

public static void Main(string[] args)
{
Console.Read();
}

public void Bug()
{
Prop.i = 2;
}
}
}
       VS2013報錯為:Error1Cannot modify the return value of 'test.Program.Prop' because it is not a variable...
       so,為什么?        在stackflow上面也查了,得到的回答大多數是說”struct is immutable“等等。但是這好像說不通,一是我這個struct沒有任何一個field聲明為readonly,完完全全是mutable的;二是immutable的話,錯誤提示的應該不是這個。        經過小小的研究和實驗,得出如下結論:
  1. 這里的Prop的get訪問控制器返回一個MyStruct對象,但是由於在C#中,struct是值類型,而get實際上就是個函數,在這里該函數返回MyStruct對象,所以這里其實返回的是個臨時對象。(class返回的是引用)。
  2. 這里調用的不是set,而是get。(set在下文的解決方案中用到)
  3. C#中,對於值類型的臨時對象而言,不允許改變,所以stackflow上面的那些大牛說的struct is immutable指的實際上是“臨時的struct對象是immutable的“。
  4. (這點和這個問題無關)struct類型的話,按照最佳的C#設計,確實應當全部設計為immutable的,即其中的所有field全部都是readonly。
       這就是為什么會報錯的原因,那么如何使用這類property呢?上代碼:
        public void Bug()
{
Prop = new MyStruct() { i = 2 };
}
       代碼編譯通過,並且實驗證明確實更改了。因為自動生成屬性中的set一般實現{ prop=value},把一個struct復制到另一個struct是可行的,所以這里成立。
       看來C#方便是方便,但是還是有些想當然的地方要靜下心來好好琢磨琢磨,說到底還是自己對代碼的感知能力不足,還是要再接再厲啊。        (2014-5-20)

2. A property which is in a struct

       這次遇到的問題是在一個struct類型的定義中,添加property后,導致一些問題。這些問題在class上也有。   根據廣大人民在網上的記錄(博客、論壇等),還有某些經典著作中,都有對比struct和class之間的區別。其中就有一條是說”strcut不需要new“。比如在《Professional C# 2012 and .NET 4.5》——即《C#高級編程》(不推薦看中文版,個別地方完全翻錯),英文版的81頁上有如下敘述:
Dimensions point;
point.Length = 3;
point.Width = 6;
        “...For a struct, however, the variable declaration actually allocates space on the stack for the entire struct,so it’s ready to assign values to."
(對於一個struct對象而言,變量的聲明實際上已經在棧上為整個struct對象分配了內存,所以已經可以給該對象賦值了) 那么我們來看以下代碼:
namespace test
{
struct MyStruct
{
public int Age { get; set; }
}

class Program
{
public static void Main(string[] args)
{
MyStruct t;
t.Age = 1;// ERROR!
Console.Read();
}
}
}
       這里有報錯,原因是"Use of unassigned local variable 't' "。看來是使用了沒有初始化的變量。如果改成MyStruct t=new MyStruct(),就正確了。那么,不是說struct類型可以不用new么?通過筆者的實驗和在stackflow上搜索資料,我得出如下結論:
如果是不使用new,而是直接 " MyStruct t; ",那么property內部封裝的對象是沒有初始化的,這里就是private int age 沒有初始化。而初始化property的方法在我所知范圍內是不存在的,對於struct,只能在構造函數中實現;而對於class,還可以通過instance field initializer,即定義的時候就public int age=1;。經過筆者嘗試,有一種辦法可以是得“MyStruct t ”合法,即手動賦值封裝的age,代碼如下:
namespace test
{
struct MyStruct
{
public int age;
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
}

class Program
{
public static void Main(string[] args)
{
MyStruct t;
<span style="background-color: rgb(255, 255, 51);">t.age = 1;</span>
Console.WriteLine(t.Age);
Console.Read();
}
}
}
       但是這樣直接破壞了封裝性,當然,struct本身一般不適用property,直接是public的field,基本沒有封裝性可言……
       其他情況下,無論如何都要顯式使用構造器和new語句。通過修改后的代碼如下:
namespace test
{
struct MyStruct
{
public int age;
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
}

class Program
{
public static void Main(string[] args)
{
<span style="background-color: rgb(255, 255, 51);">MyStruct t=new MyStruct();</span>
Console.WriteLine(t.Age);
Console.Read();
}
}
}
        代碼運行成功,結果打出0,是默認構造器的風格微笑
看來如果struct中出現了property,要顯示調用"new+構造器"才會初始化。一律都用new也是中偷懶的選擇……        這個問題基本解決,主要是實用價值不大,因為struct本來封裝性就差,帶個property怎么看都有點怪……        (2014-5-20)


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com