.NET的深復制方法(以C#語言為例)
很多時候我們復制一個對象實例A到實例B,在用實例B去做其他事情的時候,會對實例B進行修改,為保證對B的修改不會影響到A的正常使用,就需要使用到深復制。
我在網(wǎng)上搜到一些深復制的方法,同時寫了幾組例子對這些方法進行測試。
我的操作系統(tǒng)版本為Win7旗艦版,.NET Framework版本是4.5
測試程序
我建了一個C#窗體應用程序(Winform),其主窗口FormMain的Load函數(shù)內(nèi)容如下:
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類,它們的具體實現(xiàn)如下:
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; } } }
我對每個搜來的深復制方法,都用了這五個類的實例進行深復制測試,這五個類的特征如下:
I、對自定義類TestClass進行深復制測試
II、對自定義類TestClassWithS進行深復制測試,TestClassWithS是添加了Serializable特性的TestClass類
III、對DataTable進行深復制測試
IV、對控件TextBox進行深復制測試
V、對控件DataGridView進行深復制測試
我們通過實現(xiàn)方法DataManHelper.DeepCopyObject來進行測試
測試深復制方法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、觸發(fā)異常SerializationException,原因是該類不支持序列化
“System.Runtime.Serialization.SerializationException”類型的第一次機會異常在 mscorlib.dll 中發(fā)生
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、觸發(fā)異常SerializationException,原因是該類不支持序列化
“System.Runtime.Serialization.SerializationException”類型的第一次機會異常在 mscorlib.dll 中發(fā)生
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、觸發(fā)異常SerializationException,原因是該類不支持序列化
“System.Runtime.Serialization.SerializationException”類型的第一次機會異常在 mscorlib.dll 中發(fā)生
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特性時才可使用
測試深復制方法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、不會觸發(fā)異常,但結果完全錯誤
II、不會觸發(fā)異常,但結果完全錯誤
III、不會觸發(fā)異常,但結果完全錯誤
IV、Text字段賦值結果正確,但其他內(nèi)容不能保證
V、觸發(fā)異常ArgumentOutOfRangeException、TargetInvocationException
“System.ArgumentOutOfRangeException”類型的第一次機會異常在 System.Windows.Forms.dll 中發(fā)生
“System.Reflection.TargetInvocationException”類型的第一次機會異常在 mscorlib.dll 中發(fā)生
System.Reflection.TargetInvocationException: 調(diào)用的目標發(fā)生了異常。 ---> System.ArgumentOutOfRangeException: 指定的參數(shù)已超出有效值的范圍。
參數(shù)名: value
在 System.Windows.Forms.DataGridView.set_FirstDisplayedScrollingColumnIndex(Int32 value)
--- 內(nèi)部異常堆棧跟蹤的結尾 ---
在 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
結論:使用這種方法進行所謂深復制,完全是自尋死路!
測試深復制方法3
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、未觸發(fā)異常, 復制后DataTable無行列
IV、未觸發(fā)異常,Text字段未賦值
V、未觸發(fā)異常
結論:這個方法只適用于深復制具備簡單結構的類(如類中只有基礎字段、數(shù)組等),對于不支持序列化的對象也可以進行深復制。
測試深復制方法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、觸發(fā)異常MissingMethodException
“System.MissingMethodException”類型的第一次機會異常在 mscorlib.dll 中發(fā)生
System.MissingMethodException: 沒有為該對象定義無參數(shù)的構造函數(shù)。
在 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、未觸發(fā)異常,但Text字段也未賦值成功
V、觸發(fā)異常MissingMethodException
“System.MissingMethodException”類型的第一次機會異常在 mscorlib.dll 中發(fā)生
System.MissingMethodException: 沒有為該對象定義無參數(shù)的構造函數(shù)。
在 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,只能深復制基本數(shù)據(jù)類型組成的類
具體問題具體分析
從上面的例子可以看出,想找一個放之四海而皆準的方式去深復制所有對象是很困難的。一些使用高級語言特性(反射)的深復制方法,即使可以在部分類上試用成功,也無法對所有的類都具備十足的把握。因此我認為應該采取下面的方式處理對象的深復制問題:
1、對于由基本數(shù)據(jù)類型組成的類,為之打上Serializable標簽,直接使用序列化與反序列化的方法進行深復制
2、其他較為復雜的類型如DataGridView,可根據(jù)自身情況寫一個方法進行深復制,之所以在這里說要根據(jù)自身情況寫方法,是因為在對很多類進行復制時,你只需要復制對你有用的屬性就行了。如TextBox控件中,只有Text一個屬性對你是有用的,如果你需要在復制后的對象中用到Readonly等屬性的值,那么在你自己實現(xiàn)的復制方法中,也加上對這些屬性的賦值即可。這樣做還有一個好處,就是方便進行一些定制化的開發(fā)。
如下面這段代碼,就是對DataGridView的一個近似的深復制,這段代碼將一個DataGridView(dgv)的內(nèi)容復制到另一個DataGridView(dgvTmp)中,然后將dgvTmp傳遞給相關函數(shù)用于將DataGridView中的內(nèi)容輸出到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; //數(shù)據(jù)字典按顯示文字導出 } } dgvTmp.Rows.Add(objList); }
這段代碼的特點如下:
1、DataGridView的屬性AllowUserToAddRows要設置成false,否則導出到Excel文檔后,會發(fā)現(xiàn)最后會多出一個空行。
2、我們在這里標記了那些列是隱藏列,這樣在后面的處理中,如果要刪除這些列,那刪除的也是dgvTmp的列而不是dgv的列,保護了原數(shù)據(jù)。
3、對于部分數(shù)據(jù)字典的翻譯,我們傳的不是Value而是EditedFormattedValue,這種方式直接使用了dgv在屏幕上顯示的翻譯后文字,而不是原來的數(shù)據(jù)字典值。
4、對于部分金額列,需要直接傳Value值,同時需要設置該列的DefaultCellStyle.Format,這樣可使得這些內(nèi)容在之后輸出到Excel文檔后,可做求和運算(Excel中類似“12,345.67”字符串是不能做求和運算的)。
相關文章
Visual Studio C#創(chuàng)建windows服務程序
用Visual C#創(chuàng)建Windows服務不是一件困難的事,本文就將指導你一步一步創(chuàng)建一個Windows服務并使用它,本文主要介紹了Visual Studio C#創(chuàng)建windows服務程序,感興趣的可以了解一下2024-01-01C#導出pdf的實現(xiàn)方法(瀏覽器不預覽直接下載)
這篇文章主要給大家介紹了關于C#導出pdf的實現(xiàn)方法,實現(xiàn)后瀏覽器不預覽就可以直接下載,文中通過示例代碼介紹的非常詳細,對大家學習或者使用C#具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-12-12C#操作SQLite數(shù)據(jù)庫方法小結(創(chuàng)建,連接,插入,查詢,刪除等)
這篇文章主要介紹了C#操作SQLite數(shù)據(jù)庫方法,包括針對SQLite數(shù)據(jù)庫的創(chuàng)建,連接,插入,查詢,刪除等操作,并提供了一個SQLite的封裝類,需要的朋友可以參考下2016-07-07詳解C#使用AD(Active Directory)驗證內(nèi)網(wǎng)用戶名密碼
這篇文章主要介紹了詳解C#使用AD(Active Directory)驗證內(nèi)網(wǎng)用戶名密碼的相關資料,希望通過本文能幫助到大家,讓大家實現(xiàn)這樣的功能,需要的朋友可以參考下2017-10-10C#基于正則表達式實現(xiàn)獲取網(wǎng)頁中所有信息的網(wǎng)頁抓取類實例
這篇文章主要介紹了C#基于正則表達式實現(xiàn)獲取網(wǎng)頁中所有信息的網(wǎng)頁抓取類,結合完整實例形式分析了C#正則網(wǎng)頁抓取類與使用技巧,需要的朋友可以參考下2017-05-05