public AtmDataBase DeepClone() { MemoryStream ms = new MemoryStream(); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, this); ms.Position = 0; return bf.Deserialize(ms) as AtmDataBase; }
C# 支援兩種類型:“實值型別”和“參考型別”。
實值型別(Value Type)(如 char、int 和 float)、枚舉類型和結構類型。 參考型別(Reference Type) 包括類 (Class) 類型、介面類型、委託類型和數群組類型。
如何來劃分它們? 以它們在電腦記憶體中如何分配來劃分
實值型別與參考型別的區別? 1,實值型別的變數直接包含其資料, 2,參考型別的變數則儲存物件引用。 對於參考型別,兩個變數可能引用同一個對象,因此對一個變數的操作可能影響另一個變數所引用的對象。對於實值型別,每個變數都有自己的資料副本,對一個變數的操作不可能影響另一個變數。
實值型別隱式繼承自System.ValueType 所以不能顯示讓一個結構繼承一個類,C#不支援多繼承
堆棧(stack)是一種先進先出的資料結構,在記憶體中,變數會被分配在堆棧上來進行操作。 堆(heap)是用於為類型執行個體(對象)分配空間的記憶體地區,在堆上建立一個對象, 會將對象的地址傳給堆棧上的變數(反過來叫變數指向此對象,或者變數引用此對象)。
關於對象複製的所設計到知識點
淺拷貝:是指將對象中的所有欄位逐字複雜到一個新對象 對實值型別欄位只是簡單的拷貝一個副本到目標對象,改變目標對象中實值型別欄位的值不會反映到原始對象中,因為拷貝的是副本 對引用型欄位則是指拷貝他的一個引用到目標對象。改變目標對象中參考型別欄位的值它將反映到原始對象中,因為拷貝的是指向堆是上的一個地址
深拷貝:深拷貝與淺拷貝不同的是對於引用欄位的處理,深拷貝將會在新對象中建立一個新的對象和 原始對象中對應欄位相同(內容相同)的欄位,也就是說這個引用和原始對象的引用是不同, 我們改變新 對象中這個欄位的時候是不會影響到原始對象中對應欄位的內容。
淺複製: 實現淺複製需要使用Object類的MemberwiseClone方法用於建立一個淺表副本 深複製: 須實現 ICloneable介面中的Clone方法,且需要需要複製的對象加上[Serializable]特性
class DrawBase : System.Object, ICloneable {
public string name = "jmj";
public DrawBase() { }
public object Clone() {
return this as object; //引用同一個對象
return this.MemberwiseClone(); //淺複製
return new DrawBase() as object;//深複製
}
}
class Program {
static void Main(string[] args) {
DrawBase rect = new DrawBase();
Console.WriteLine(rect.name);
DrawBase line = rect.Clone() as DrawBase;
line.name = "a9fs3";
Console.WriteLine(rect.name);
DrawBase ploy = line.Clone() as DrawBase;
ploy.name = "lj";
Console.WriteLine(rect.name);
Console.WriteLine(object.ReferenceEquals(line, ploy));
Console.ReadLine();
}
}
運行結果: return this as object; //引用同一個對象
輸出:jmj a9fs3 lj True
return this.MemberwiseClone(); //淺複製
return new DrawBase() as object;//深複製
輸出均為: jmj jmj jmj False
解釋: return this as object 方法總是引用同一個對象,因此相應的堆記憶體上的值會改變! 後兩種方法都是對對象的複製,區別在於複製的類別不同:深複製會複製整個填充的對象,包括該對象中其他參考型別和實值型別的值;而淺複製只複製了一個對象中所有引用,它沒有值的複製,通過引用它們的其他對象的引用來共用它們。
--------------------------------------------------------------------------------------------------------
雖然在現實世界中的複製課題是有爭議的, 在.NET世界使用它卻足夠安全, 難道不是嗎?
為實現一個類你究竟有多少次要實現ICloneable介面, 而且每一次都寫相同的代碼,或為每個類寫特定的代碼。
而且,當你的類加入一個新的欄位時,往往會忘記更新這個新欄位的複製方法。如果我沒說錯的話,這種時候往往會帶來惱人的bugs。
這是我的類得以存在的原因。 藉由反射機制的小小協助,我建立了一個用預設行為實現了ICloneable介面的抽象類別。
現在或許你正在問自己: 什麼是預設行為? 那麼我很高興你這樣詢問。 複製的預設行為,是採用以下的規則來複製類中的每一個欄位:
查看一下類中的每一個欄位是否支援ICloneable介面
如果某欄位不支援ICloneable介面,那麼該欄位將以常規方式處理。
這意味著,如果該欄位是一個實值型別,那麼該值被拷貝;如果該欄位是一個參考型別,複製的欄位將指向同一個對象。
如果該欄位支援ICloneable介面,我們將使用其本身的Clone方法對其進行複製。
如果該欄位支援IEnumerable介面,我們需要檢查他是否支援IList 或 IDictionary 介面。
如果支援,那麼我們迭代該集件,並且查看集合的每一項是否支援ICloneable介面。
如何使用
讓你的類支援Icloneable介面所要做的就是,將你的類繼承自如下所述的BaseObject類:
public class MyClass : BaseObject
{
public string myStr =”test”;
public int id;
}
public class MyContainer : BaseObject
{
public string name = “test2”;
public MyClass[] myArray= new MyClass[5];
public class MyContainer()
{
for(int i=0 ; i<5 ; i++)
{
this.myArray[I] = new MyClass();
}
}
}
現在在Main方法中加入如下代碼:
static void Main(string[] args){
MyContainer con1 = new MyContainer();
MyContainer con2 = (MyContainer)con1.Clone();
con2.myArray[0].id = 5;
}
當監測con2執行個體時,你將會看到MyClass執行個體的第一項已經變為5,而con1執行個體卻沒有改變。
這樣你將明白加入到類中的任意支援ICloneable介面的欄位將被同樣地複製。
而且,如果該欄位支援IList 或 IDictionary 介面,複製方法將偵測該欄位,輪詢所有項,並同樣地試圖對他們進行複製。
實現
/// <summary>
/// BaseObject類是一個用來繼承的抽象類別。
/// 每一個由此類繼承而來的類將自動支援複製方法。
/// 該類實現了Icloneable介面,並且每個從該對象繼承而來的對象都將同樣地
/// 支援Icloneable介面。
/// </summary>
public abstract class BaseObject : ICloneable
{
/// <summary>
/// 複製對象,並返回一個已複製對象的引用
/// </summary>
/// <returns>引用新的複製對象</returns>
public object Clone()
{
//首先我們建立指定類型的一個執行個體
object newObject = Activator.CreateInstance(this.GetType());
//我們取得新的類型執行個體的欄位數組。
FieldInfo[] fields = newObject.GetType().GetFields();
int i = 0;
foreach (FieldInfo fi in this.GetType().GetFields())
{
//我們判斷欄位是否支援ICloneable介面。
Type ICloneType = fi.FieldType.
GetInterface("ICloneable", true);
if (ICloneType != null)
{
//取得對象的Icloneable介面。
ICloneable IClone = (ICloneable)fi.GetValue(this);
//我們使用複製方法給欄位設定新值。
fields[i].SetValue(newObject, IClone.Clone());
}
else
{
// 如果該欄位部支援Icloneable介面,直接設定即可。
fields[i].SetValue(newObject, fi.GetValue(this));
}
//現在我們檢查該對象是否支援IEnumerable介面,如果支援,
//我們還需要枚舉其所有項並檢查他們是否支援IList 或 IDictionary 介面。
Type IEnumerableType = fi.FieldType.GetInterface("IEnumerable", true);
if (IEnumerableType != null)
{
//取得該欄位的IEnumerable介面
IEnumerable IEnum = (IEnumerable)fi.GetValue(this);
//這個版本支援IList 或 IDictionary 介面來迭代集合。
Type IListType = fields[i].FieldType.GetInterface("IList", true);
Type IDicType = fields[i].FieldType.GetInterface("IDictionary", true);
int j = 0;
if (IListType != null)
{
//取得IList介面。
IList list = (IList)fields[i].GetValue(newObject);
foreach (object obj in IEnum)
{
//查看當前項是否支援支援ICloneable 介面。
ICloneType = obj.GetType().
GetInterface("ICloneable", true);
if (ICloneType != null)
{
//如果支援ICloneable 介面,
//我們用它李設定列表中的對象的複製
ICloneable clone = (ICloneable)obj;
list[j] = clone.Clone();
}
//注意:如果列表中的項不支援ICloneable介面,那麼
//在複製列表的項將與原列表對應項相同
//(只要該類型是參考型別)
j++;
}
}
else if (IDicType != null)
{
//取得IDictionary 介面
IDictionary dic = (IDictionary)fields[i].
GetValue(newObject);
j = 0;
foreach (DictionaryEntry de in IEnum)
{
//查看當前項是否支援支援ICloneable 介面。
ICloneType = de.Value.GetType().
GetInterface("ICloneable", true);
if (ICloneType != null)
{
ICloneable clone = (ICloneable)de.Value;
dic[de.Key] = clone.Clone();
}
j++;
}
}
}
i++;
}
return newObject;
}
}