以C#語言爲例討論幾種.NET的深複製方法

1、寫在前面

深複製須要將對象實例中字段引用的對象也進行復制,在平時的編程工做中常常要用到這種複製方式,由於不少時候咱們複製一個對象實例A到實例B,在用實例B去作其餘事情的時候,會對實例B進行修改,爲保證對B的修改不會影響到A的正常使用,就須要使用到深複製。編程

我在網上搜到一些深複製的方法,同時寫了幾組例子對這些方法進行測試。數組

個人操做系統版本爲Win7旗艦版,.NET Framework版本是4.5ide

2、測試程序

我建了一個C#窗體應用程序(Winform),其主窗口FormMain的Load函數內容以下:函數

private void FormMain_Load(object sender, EventArgs e)
{
    //測試1:深度複製 自定義類
    try
    {
        Console.WriteLine("=== 深度複製 自定義類 ===");
        TestClass test1 = new TestClass();
        test1.a = 10;
        test1.b = "hello world!";
        test1.c = new string[] { "x", "y", "z" };
        TestClass test2 = new TestClass();
        test2.a = 11;
        test2.b = "hello world2!";
        test2.c = new string[] { "i", "j", "k" };
        test1.d = test2;
        Console.WriteLine("---test1_start---");
        Console.WriteLine(test1);
        Console.WriteLine("---test1_end---");
        TestClass test3 = (TestClass)DataManHelper.DeepCopyObject(test1);
        Console.WriteLine("---test3_start---");
        Console.WriteLine(test3);
        Console.WriteLine("---test3_end---");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    //測試2:深度複製 可序列化的自定義類
    try
    {
        Console.WriteLine("=== 深度複製 可序列化的自定義類 ===");
        TestClassWithS test1 = new TestClassWithS();
        test1.a = 10;
        test1.b = "hello world!";
        test1.c = new string[] { "x", "y", "z" };
        TestClassWithS test2 = new TestClassWithS();
        test2.a = 11;
        test2.b = "hello world2!";
        test2.c = new string[] { "i", "j", "k" };
        test1.d = test2;
        Console.WriteLine("---test1_start---");
        Console.WriteLine(test1);
        Console.WriteLine("---test1_end---");
        TestClassWithS test3 = (TestClassWithS)DataManHelper.DeepCopyObject(test1);
        Console.WriteLine("---test3_start---");
        Console.WriteLine(test3);
        Console.WriteLine("---test3_end---");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    //測試3:深度複製 DataTable
    try
    {
        Console.WriteLine("=== 深度複製 DataTable ===");
        DataTable dtKirov = new DataTable("TestTable");
        dtKirov.Columns.Add("Col1");
        dtKirov.Columns.Add("Col2");
        dtKirov.Columns.Add("Col3");
        dtKirov.Rows.Add("1-1", "1-2", "1-3");
        dtKirov.Rows.Add("2-1", "2-2", "2-3");
        dtKirov.Rows.Add("3-1", "3-2", "3-3");
        Console.WriteLine("=== 複製前 ===");
        for (int i = 0; i < dtKirov.Columns.Count; i++)
        {
            Console.Write(dtKirov.Columns[i].ColumnName + "\t");
        }
        Console.WriteLine("\n-----------------");
        for (int i = 0; i < dtKirov.Columns.Count; i++)
        {
            for (int j = 0; j < dtKirov.Rows.Count; j++)
            {
                Console.Write(dtKirov.Rows[i][j].ToString() + "\t");
            }
            Console.WriteLine();
        }
        Console.WriteLine();
        DataTable dtDreadNought = (DataTable)DataManHelper.DeepCopyObject(dtKirov);
        Console.WriteLine("=== 複製後 ===");
        for (int i = 0; i < dtDreadNought.Columns.Count; i++)
        {
            Console.Write(dtDreadNought.Columns[i].ColumnName + "\t");
        }
        Console.WriteLine("\n-----------------");
        for (int i = 0; i < dtDreadNought.Columns.Count; i++)
        {
            for (int j = 0; j < dtDreadNought.Rows.Count; j++)
            {
                Console.Write(dtDreadNought.Rows[i][j].ToString() + "\t");
            }
            Console.WriteLine();
        }
        Console.WriteLine();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    //測試4:深度複製 TextBox
    try
    {
        Console.WriteLine("=== 深度複製 TextBox ===");
        txtTest.Text = "1234";
        Console.WriteLine("複製前:" + txtTest.Text);
        TextBox txtTmp = new TextBox();
        txtTmp = (TextBox)DataManHelper.DeepCopyObject(txtTest);
        Console.WriteLine("複製後:" + txtTmp.Text);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    //測試5:深度複製 DataGridView
    try
    {
        Console.WriteLine("=== 深度複製 DataGridView ===");
        DataGridView dgvTmp = new DataGridView();
        dgvTmp = (DataGridView)DataManHelper.DeepCopyObject(dgvTest);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

其中txtTest是一個測試用的TextBox,dgvTmp是一個測試用的DataGridView,TestClass是一個自定義類,TestClassWithS是添加了Serializable特性的TestClass類,它們的具體實現以下:測試

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataCopyTest
{
    public class TestClass
    {
        public int a;
        public string b;
        public string[] c;
        public TestClass d;
        public override string ToString()
        {
            string s = "a:" + a + "\n";
            if (b != null)
            {
                s += "b:" + b + "\n";
            }
            if (c != null)
            {
                foreach (string tmps in c)
                {
                    if (!string.IsNullOrWhiteSpace(tmps))
                    {
                        s += "c:" + tmps + "\n";
                    }
                }
            }
            if (d != null)
            {
                s += d.ToString();
            }
            return s;
        }
    }

    //支持序列化的TestClass
    [Serializable]
    public class TestClassWithS
    {
        public int a;
        public string b;
        public string[] c;
        public TestClassWithS d;
        public override string ToString()
        {
            string s = "a:" + a + "\n";
            if (b != null)
            {
                s += "b:" + b + "\n";
            }
            if (c != null)
            {
                foreach (string tmps in c)
                {
                    if (!string.IsNullOrWhiteSpace(tmps))
                    {
                        s += "c:" + tmps + "\n";
                    }
                }
            }
            if (d != null)
            {
                s += d.ToString();
            }
            return s;
        }
    }
}

我對每一個搜來的深複製方法,都用了這五個類的實例進行深複製測試,這五個類的特徵以下:spa

I、對自定義類TestClass進行深複製測試操作系統

II、對自定義類TestClassWithS進行深複製測試,TestClassWithS是添加了Serializable特性的TestClass類翻譯

III、對DataTable進行深複製測試code

IV、對控件TextBox進行深複製測試orm

V、對控件DataGridView進行深複製測試

咱們經過實現方法DataManHelper.DeepCopyObject來進行測試

3、測試深複製方法1

使用二進制流的序列化與反序列化深度複製對象

public static object DeepCopyObject(object obj)
{
    BinaryFormatter Formatter = new BinaryFormatter(null, 
        new StreamingContext(StreamingContextStates.Clone));
    MemoryStream stream = new MemoryStream();
    Formatter.Serialize(stream, obj);
    stream.Position = 0;
    object clonedObj = Formatter.Deserialize(stream);
    stream.Close();
    return clonedObj;
}

五個場景的測試結果爲:

I、觸發異常SerializationException,緣由是該類不支持序列化

「System.Runtime.Serialization.SerializationException」類型的第一次機會異常在 mscorlib.dll 中發生
System.Runtime.Serialization.SerializationException: 程序集「DataCopyTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null」中的類型「DataCopyTest.TestClass」未標記爲可序列化。
   在 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
   在 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, S「DataCopyTest.vshost.exe」(託管(v4.0.30319)): 已加載「C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll」
erializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行號 24
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行號 37

II、可正常複製 (√)

III、可正常複製 (√)

IV、觸發異常SerializationException,緣由是該類不支持序列化

「System.Runtime.Serialization.SerializationException」類型的第一次機會異常在 mscorlib.dll 中發生
System.Runtime.Serialization.SerializationException: 程序集「System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089」中的類型「System.Windows.Forms.TextBox」未標記爲可序列化。
   在 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
   在 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行號 24
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行號 128

V、觸發異常SerializationException,緣由是該類不支持序列化

「System.Runtime.Serialization.SerializationException」類型的第一次機會異常在 mscorlib.dll 中發生
System.Runtime.Serialization.SerializationException: 程序集「System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089」中的類型「System.Windows.Forms.DataGridView」未標記爲可序列化。
   在 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
   在 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行號 24
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行號 141

結論:利用序列化與反序列化到二進制流的方法深複製對象,只有在該對象支持Serializable特性時纔可以使用

4、測試深複製方法2

public static object DeepCopyObject(object obj)
{
    Type t = obj.GetType();
    PropertyInfo[] properties = t.GetProperties();
    Object p = t.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, obj, null);
    foreach (PropertyInfo pi in properties)
    {
        if (pi.CanWrite)
        {
            object value = pi.GetValue(obj, null);
            pi.SetValue(p, value, null);
        }
    }
    return p;
}

五個場景的測試結果爲:

I、不會觸發異常,但結果徹底錯誤

II、不會觸發異常,但結果徹底錯誤

III、不會觸發異常,但結果徹底錯誤

IV、Text字段賦值結果正確,但其餘內容不能保證

V、觸發異常ArgumentOutOfRangeException、TargetInvocationException

「System.ArgumentOutOfRangeException」類型的第一次機會異常在 System.Windows.Forms.dll 中發生
「System.Reflection.TargetInvocationException」類型的第一次機會異常在 mscorlib.dll 中發生
System.Reflection.TargetInvocationException: 調用的目標發生了異常。 ---> System.ArgumentOutOfRangeException: 指定的參數已超出有效值的範圍。
參數名: value
   在 System.Windows.Forms.DataGridView.set_FirstDisplayedScrollingColumnIndex(Int32 value)
   --- 內部異常堆棧跟蹤的結尾 ---
   在 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   在 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   在 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
   在 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行號 29
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行號 141

結論:使用這種方法進行所謂深複製,徹底是自尋死路!

5、測試深複製方法3

這段代碼找自頁面:http://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-an-object-in-net-c-specifically

public static object DeepCopyObject(object obj)
{
    if (obj != null)
    {
        object result = Activator.CreateInstance(obj.GetType());
        foreach (FieldInfo field in obj.GetType().GetFields())
        {
            if (field.FieldType.GetInterface("IList", false) == null)
            {
                field.SetValue(result, field.GetValue(obj));
            }
            else
            {
                IList listObject = (IList)field.GetValue(result);
                if (listObject != null)
                {
                    foreach (object item in ((IList)field.GetValue(obj)))
                    {
                        listObject.Add(DeepCopyObject(item));
                    }
                }
            }
        }
        return result;
    }
    else
    {
        return null;
    }
}

五個場景的測試結果爲:

I、可正常複製(√)

II、可正常複製(√)

III、未觸發異常, 複製後DataTable無行列

IV、未觸發異常,Text字段未賦值

V、未觸發異常

結論:這個方法只適用於深複製具有簡單結構的類(如類中只有基礎字段、數組等),對於不支持序列化的對象也能夠進行深複製。

6、測試深複製方法4

這段代碼來源同方法3

public static object DeepCopyObject(object obj)
{
    if (obj == null)
        return null;
    Type type = obj.GetType();

    if (type.IsValueType || type == typeof(string))
    {
        return obj;
    }
    else if (type.IsArray)
    {
        Type elementType = Type.GetType(
                type.FullName.Replace("[]", string.Empty));
        var array = obj as Array;
        Array copied = Array.CreateInstance(elementType, array.Length);
        for (int i = 0; i < array.Length; i++)
        {
            copied.SetValue(DeepCopyObject(array.GetValue(i)), i);
        }
        return Convert.ChangeType(copied, obj.GetType());
    }
    else if (type.IsClass)
    {

        object toret = Activator.CreateInstance(obj.GetType());
        FieldInfo[] fields = type.GetFields(BindingFlags.Public |
                    BindingFlags.NonPublic | BindingFlags.Instance);
        foreach (FieldInfo field in fields)
        {
            object fieldValue = field.GetValue(obj);
            if (fieldValue == null)
                continue;
            field.SetValue(toret, DeepCopyObject(fieldValue));
        }
        return toret;
    }
    else
        throw new ArgumentException("Unknown type");
}

五個場景的測試結果爲:

I、可正常複製(√)

II、可正常複製(√)

III、觸發異常MissingMethodException

「System.MissingMethodException」類型的第一次機會異常在 mscorlib.dll 中發生
System.MissingMethodException: 沒有爲該對象定義無參數的構造函數。
   在 System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   在 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   在 System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   在 System.Activator.CreateInstance(Type type, Boolean nonPublic)
   在 System.Activator.CreateInstance(Type type)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行號 45
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行號 53
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行號 99

IV、未觸發異常,但Text字段也未賦值成功

V、觸發異常MissingMethodException

「System.MissingMethodException」類型的第一次機會異常在 mscorlib.dll 中發生
System.MissingMethodException: 沒有爲該對象定義無參數的構造函數。
   在 System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   在 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   在 System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   在 System.Activator.CreateInstance(Type type, Boolean nonPublic)
   在 System.Activator.CreateInstance(Type type)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行號 45
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行號 53
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行號 141

結論:這個方法的做用相似方法3,只能深複製基本數據類型組成的類

7、具體問題具體分析

從上面的例子能夠看出,想找一個放之四海而皆準的方式去深複製全部對象是很困難的。一些使用高級語言特性(反射)的深複製方法,即便能夠在部分類上試用成功,也沒法對全部的類都具有十足的把握。所以我認爲應該採起下面的方式處理對象的深複製問題:

一、對於由基本數據類型組成的類,爲之打上Serializable標籤,直接使用序列化與反序列化的方法進行深複製

二、其餘較爲複雜的類型如DataGridView,可根據自身狀況寫一個方法進行深複製,之因此在這裏說要根據自身狀況寫方法,是由於在對不少類進行復制時,你只須要複製對你有用的屬性就好了。如TextBox控件中,只有Text一個屬性對你是有用的,若是你須要在複製後的對象中用到Readonly等屬性的值,那麼在你本身實現的複製方法中,也加上對這些屬性的賦值便可。這樣作還有一個好處,就是方便進行一些定製化的開發。

以下面這段代碼,就是對DataGridView的一個近似的深複製,這段代碼將一個DataGridView(dgv)的內容複製到另外一個DataGridView(dgvTmp)中,而後將dgvTmp傳遞給相關函數用於將DataGridView中的內容輸出到Excel文檔:

DataGridView dgvTmp = new DataGridView();
dgvTmp.AllowUserToAddRows = false; //不容許用戶生成行,不然導出後會多出最後一行
for (int i = 0; i < dgv.Columns.Count; i++)
{
	dgvTmp.Columns.Add(dgv.Columns[i].Name, dgv.Columns[i].HeaderText);
	if (dgv.Columns[i].DefaultCellStyle.Format.Contains("N")) //使導出Excel文檔金額列可作SUM運算
	{
		dgvTmp.Columns[i].DefaultCellStyle.Format = dgv.Columns[i].DefaultCellStyle.Format;
	}
	if (!dgv.Columns[i].Visible)
	{
		dgvTmp.Columns[i].Visible = false;
	}
}
for (int i = 0; i < dgv.Rows.Count; i++)
{
	object[] objList = new object[dgv.Rows[i].Cells.Count];
	for (int j = 0; j < objList.Length; j++)
	{
		if (dgvTmp.Columns[j].DefaultCellStyle.Format.Contains("N"))
		{
			objList[j] = dgv.Rows[i].Cells[j].Value; //使導出Excel文檔金額列可作SUM運算
		}
		else
		{
			objList[j] = dgv.Rows[i].Cells[j].EditedFormattedValue; //數據字典按顯示文字導出
		}
	}
	dgvTmp.Rows.Add(objList);
}

這段代碼的特色以下:

一、DataGridView的屬性AllowUserToAddRows要設置成false,不然導出到Excel文檔後,會發現最後會多出一個空行。

二、咱們在這裏標記了那些列是隱藏列,這樣在後面的處理中,若是要刪除這些列,那刪除的也是dgvTmp的列而不是dgv的列,保護了原數據。

三、對於部分數據字典的翻譯,咱們傳的不是Value而是EditedFormattedValue,這種方式直接使用了dgv在屏幕上顯示的翻譯後文字,而不是原來的數據字典值。

四、對於部分金額列,須要直接傳Value值,同時須要設置該列的DefaultCellStyle.Format,這樣可以使得這些內容在以後輸出到Excel文檔後,可作求和運算(Excel中相似「12,345.67」字符串是不能作求和運算的)。

END

相關文章
相關標籤/搜索