System.Data.DataTable 提供兩個易混淆的方法:Clone() 與 Copy()。兩者皆回傳新的 DataTable 實例,但複製深度截然不同。本文從定義、程式碼驗證、記憶體行為、實務適用場景完整剖析。
| 方法 | 複製內容 | 資料列 | 約束/關聯 | 典型用途 |
|---|---|---|---|---|
Clone() |
僅結構:欄位定義、資料型別、UniqueConstraint、ForeignKeyConstraint、ExtendedProperties |
無 (空表) | 是 | 建立相同結構的空表、效能優於 Copy、遷移 Schema |
Copy() |
結構 + 所有資料:含每一筆 DataRow、RowState、RowError |
完整複製 (深層) | 是 | 備份、離線編輯後合併、跨 Thread 傳遞資料快照 |
using System;
using System.Data;
using System.Data.SqlClient;
class DataTableCloneCopyDemo
{
static string ConnStr = "Server=.\\SQLEXPRESS;Database=TestDB;Integrated Security=True;";
static void Main()
{
using (var conn = new SqlConnection(ConnStr))
using (var da = new SqlDataAdapter("SELECT TOP 5 Id, Name, Age FROM Users", conn))
{
var dt = new DataTable();
da.Fill(dt);
Console.WriteLine($"原始表: Rows={dt.Rows.Count}, Columns={dt.Columns.Count}");
// Clone
var dtClone = dt.Clone();
Console.WriteLine($"Clone(): Rows={dtClone.Rows.Count}, Columns={dtClone.Columns.Count}");
PrintSchema(dtClone);
// Copy
var dtCopy = dt.Copy();
Console.WriteLine($"Copy(): Rows={dtCopy.Rows.Count}, Columns={dtCopy.Columns.Count}");
PrintData(dtCopy);
}
}
static void PrintSchema(DataTable t)
{
foreach (DataColumn c in t.Columns)
Console.Write($" {c.ColumnName}({c.DataType.Name})");
Console.WriteLine();
}
static void PrintData(DataTable t)
{
foreach (DataRow r in t.Rows)
Console.WriteLine($" {r[0]}\t{r[1]}\t{r[2]}");
}
}
原始表: Rows=5, Columns=3 Clone(): Rows=0, Columns=3 Id(Int32) Name(String) Age(Int32) Copy(): Rows=5, Columns=3 1 Alice 25 2 Bob 30 3 Carol 28 4 Dave 35 5 Eve 22
Copy() 保留 Added/Modified/Deleted/Unchanged 狀態,適合離線編輯後 Merge() 回主表。若需「複製結構 + 部分資料」,不要 先 Copy() 再刪除列,應用 Clone() + ImportRow():
// 需求:複製 Age > 30 的列
var filtered = dt.Clone(); // 只要結構
foreach (DataRow row in dt.Select("Age > 30"))
{
filtered.ImportRow(row); // 深層複製單列 (含 RowState)
}
// filtered 現在只有符合條件的列,效能遠優於 Copy() + Delete()
需要新 DataTable
│
├─ 只要結構 (建立暫存表、定義 Schema) → Clone()
│
└─ 需要資料
│
├─ 全部資料 (備份、快照、跨域傳遞) → Copy()
│
└─ 部分資料 (篩選、分頁、條件複製) → Clone() + ImportRow() / LoadDataRow()
Copy() 是深層複製,修改 dtCopy.Rows[0]["Name"] 不會影響 dt。但若欄位型別為參考型別 (如 byte[]、自訂類別),則複製的是參考,修改物件內容會互相影響。Clone() + ImportRow 流式處理。Constraints 集合,但 ForeignKeyConstraint 參考的父表若不在同一 DataSet 會失效。
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// 1. 從 DB 載入主表
DataTable master = GetDataFromDB();
Session["MasterTable"] = master;
gv.DataSource = master;
gv.DataBind();
}
}
protected void gv_RowEditing(object sender, GridViewEditEventArgs e)
{
gv.EditIndex = e.NewEditIndex;
gv.DataSource = Session["MasterTable"];
gv.DataBind();
}
protected void gv_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
DataTable master = (DataTable)Session["MasterTable"];
DataRow row = master.Rows[e.RowIndex];
// 2. 直接更新主表 (或 Clone 一份離線編輯)
row["Name"] = e.NewValues["Name"];
row["Age"] = e.NewValues["Age"];
gv.EditIndex = -1;
gv.DataSource = master;
gv.DataBind();
}
// 若需「取消所有變更」功能:
protected void btnRevert_Click(object sender, EventArgs e)
{
// 3. 從 Session 取得原始快照 (Copy)
DataTable original = ((DataTable)Session["MasterTable"]).Copy();
Session["MasterTable"] = original;
gv.DataSource = original;
gv.DataBind();
}
Clone() = Schema 只有,極快、記憶體小,適用建立空表、篩選複製基底。Copy() = Schema + Data 完整,深層複製含 RowState,適用備份、快照、離線編輯。Clone() + ImportRow(),避免 Copy() 後刪除列的效能浪費。