C#內(nèi)置泛型委托之Func委托
一、什么是Func委托
Func委托代表有返回類型的委托
二、Func委托定義
查看Func的定義:
using System.Runtime.CompilerServices; namespace System { // // 摘要: // 封裝一個方法,該方法具有兩個參數(shù),并返回由 TResult 參數(shù)指定的類型的值。 // // 參數(shù): // arg1: // 此委托封裝的方法的第一個參數(shù)。 // // arg2: // 此委托封裝的方法的第二個參數(shù)。 // // 類型參數(shù): // T1: // 此委托封裝的方法的第一個參數(shù)的類型。 // // T2: // 此委托封裝的方法的第二個參數(shù)的類型。 // // TResult: // 此委托封裝的方法的返回值類型。 // // 返回結(jié)果: // 此委托封裝的方法的返回值。 [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")] public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2); }
你會發(fā)現(xiàn),F(xiàn)unc其實就是有多個輸出參數(shù)并且有返回值的delegate。
3、示例
Func至少0個輸入?yún)?shù),至多16個輸入?yún)?shù),根據(jù)返回值泛型返回。必須有返回值,不可void。
Func<int> 表示沒有輸入?yún)?,返回值為int類型的委托。
Func<object,string,int> 表示傳入?yún)?shù)為object, string ,返回值為int類型的委托。
Func<object,string,int> 表示傳入?yún)?shù)為object, string, 返回值為int類型的委托。
Func<T1,T2,,T3,int> 表示傳入?yún)?shù)為T1,T2,,T3(泛型),返回值為int類型的委托。
代碼示例如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FunDemo { class Program { static void Main(string[] args) { // 無參數(shù),只要返回值 Func<int> fun1 = new Func<int>(FunWithNoPara); int result1= fun1(); Console.WriteLine(result1); Console.WriteLine("----------------------------"); Func<int> fun2 = delegate { return 19; }; int result2 = fun2(); Console.WriteLine(result2); Console.WriteLine("----------------------------"); Func<int> fun3 = () => { return 3; }; int result3 = fun3(); Console.WriteLine(result3); Console.WriteLine("----------------------------"); //有一個參數(shù),一個返回值 Func<int, int> fun4 = new Func<int, int>(FunWithPara); int result4 = fun4(4); Console.WriteLine($"這里是一個參數(shù)一個返回值的方法,返回值是:{result4}"); Console.WriteLine("----------------------------"); // 使用委托 Func<int, string> fun5 = delegate (int i) { return i.ToString(); }; string result5 = fun5(5); Console.WriteLine($"這里是一個參數(shù)一個返回值的委托,返回值是:{result5}"); Console.WriteLine("----------------------------"); // 使用匿名委托 Func<int, string> fun6 = (int i) => { return i.ToString(); }; string result6 = fun6(6); Console.WriteLine($"這里是一個參數(shù)一個返回值的匿名委托,返回值是:{result6}"); Console.WriteLine("----------------------------"); // 多個輸入?yún)?shù) Func<int, string, bool> fun7 = new Func<int, string, bool>(FunWithMultiPara); bool result7 = fun7(2, "2"); Console.WriteLine($"這里是有多個輸入?yún)?shù)的方法,返回值是:{result7}"); Console.WriteLine("----------------------------"); // 使用委托 Func<int, string, bool> fun8 = delegate (int i, string s) { return i.ToString().Equals(s) ? true : false; }; bool result8 = fun8(2, "abc"); Console.WriteLine($"這里是有多個輸入?yún)?shù)的委托,返回值是:{result8}"); Console.WriteLine("----------------------------"); // 使用匿名委托 Func<int, string, bool> fun9 = (int i, string s) => { return i.ToString().Equals(s) ? true : false; }; bool result9 = fun9(45, "ert"); Console.WriteLine($"這里是有多個輸入?yún)?shù)的匿名委托,返回值是:{result9}"); Console.ReadKey(); } static int FunWithNoPara() { return 10; } static int FunWithPara(int i) { return i; } static bool FunWithMultiPara(int i,string s) { return i.ToString().Equals(s) ? true : false; } } }
運行結(jié)果:
4、真實示例
在下面的示例中,利用Func委托封裝數(shù)據(jù)庫通用訪問類。
1、定義BaseModel基類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FunApplication.Model { public class BaseModel { public int Id { get; set; } } }
2、定義Student類繼承自BaseModel基類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FunApplication.Model { public class Student : BaseModel { public string Name { get; set; } public int Age { get; set; } public int Sex { get; set; } public string Email { get; set; } } }
3、定義數(shù)據(jù)庫訪問方法接口
using FunApplication.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FunApplication.IDAL { public interface IBaseDAL { T Query<T>(int id) where T : BaseModel; List<T> QueryAll<T>() where T : BaseModel; int Insert<T>(T t) where T : BaseModel; int Update<T>(T t) where T : BaseModel; int Delete<T>(int id) where T : BaseModel; } }
4、定義屬性幫助類
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace FunApplication.AttributeExtend { public static class AttributeHelper { public static string GetColumnName(this PropertyInfo prop) { if (prop.IsDefined(typeof(ColumnAttribute), true)) { ColumnAttribute attribute = (ColumnAttribute)prop.GetCustomAttribute(typeof(ColumnAttribute), true); return attribute.GetColumnName(); } else { return prop.Name; } } } }
5、定義ColumnAttribute類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FunApplication.AttributeExtend { [AttributeUsage(AttributeTargets.Property)] public class ColumnAttribute : Attribute { public ColumnAttribute(string name) { this._Name = name; } private string _Name = null; public string GetColumnName() { return this._Name; } } }
6、定義數(shù)據(jù)庫方法接口實現(xiàn)類
using FunApplication.IDAL; using FunApplication.Model; using System; using System.Collections.Generic; using System.Configuration; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Reflection; using FunApplication.AttributeExtend; namespace FunApplication.DAL { public class BaseDAL : IBaseDAL { // 數(shù)據(jù)庫鏈接字符串 private static string strConn = ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString; public int Delete<T>(int id) where T : BaseModel { int result = 0; using (SqlConnection conn = new SqlConnection(strConn)) { string strSQL = "delete from Student where Id=@Id"; SqlParameter para = new SqlParameter("Id", id); SqlCommand command = new SqlCommand(strSQL, conn); command.Parameters.Add(para); conn.Open(); result = command.ExecuteNonQuery(); } return result; } public int Insert<T>(T t) where T : BaseModel { int result = 0; using (SqlConnection conn = new SqlConnection(strConn)) { Type type = typeof(T); var propArray = type.GetProperties().Where(p => p.Name != "Id"); string strSQL = "insert into Student Values (@Name,@Age,@Sex,@Email) "; SqlCommand command = new SqlCommand(strSQL, conn); var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray(); command.Parameters.AddRange(parameters); conn.Open(); result = command.ExecuteNonQuery(); } return result; } public T Query<T>(int id) where T : BaseModel { Type type = typeof(T); string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]")); string sql = $"SELECT {columnString} FROM [{type.Name}] WHERE Id={id}"; T t = null;// (T)Activator.CreateInstance(type); using (SqlConnection conn = new SqlConnection(strConn)) { SqlCommand command = new SqlCommand(sql, conn); conn.Open(); SqlDataReader reader = command.ExecuteReader(); List<T> list = this.ReaderToList<T>(reader); t = list.FirstOrDefault(); } return t; } public List<T> QueryAll<T>() where T : BaseModel { Type type = typeof(T); string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]")); string sql = $"SELECT {columnString} FROM [{type.Name}] "; List<T> list = new List<T>(); using (SqlConnection conn = new SqlConnection(strConn)) { SqlCommand command = new SqlCommand(sql, conn); conn.Open(); SqlDataReader reader = command.ExecuteReader(); list = this.ReaderToList<T>(reader); } return list; } public int Update<T>(T t) where T : BaseModel { int result = 0; using (SqlConnection conn = new SqlConnection(strConn)) { Type type = typeof(T); var propArray = type.GetProperties().Where(p => p.Name != "Id"); string columnString = string.Join(",", propArray.Select(p => $"[{p.GetColumnName()}]=@{p.GetColumnName()}")); var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray(); //必須參數(shù)化 否則引號? 或者值里面還有引號 string strSQL = $"UPDATE [{type.Name}] SET {columnString} WHERE Id={t.Id}"; SqlCommand command = new SqlCommand(strSQL, conn); command.Parameters.AddRange(parameters); conn.Open(); result = command.ExecuteNonQuery(); } return result; } private List<T> ReaderToList<T>(SqlDataReader reader) where T : BaseModel { Type type = typeof(T); List<T> list = new List<T>(); while (reader.Read())//表示有數(shù)據(jù) 開始讀 { T t = (T)Activator.CreateInstance(type); foreach (var prop in type.GetProperties()) { object oValue = reader[prop.GetColumnName()]; if (oValue is DBNull) oValue = null; prop.SetValue(t, oValue);//除了guid和枚舉 } list.Add(t); } return list; } } }
7、在Main()方法中調(diào)用
using FunApplication.DAL; using FunApplication.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FunApplication { class Program { static void Main(string[] args) { #region MyRegion BaseDAL dal = new BaseDAL(); // 查詢 Student student = dal.Query<Student>(2); Console.WriteLine($"姓名:{student.Name},年齡:{student.Age},Email地址:{student.Email}"); Console.WriteLine("----------------------------"); // 查詢所有 List<Student> list = dal.QueryAll<Student>(); Console.WriteLine($"集合個數(shù):{list.Count}"); Console.WriteLine("----------------------------"); // 插入 Student studentIns = new Student() { Name = "小明", Age = 20, Sex = 2, Email = "xiaoming@qq.com" }; bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false; Console.WriteLine($"插入執(zhí)行結(jié)果:{resultIns}"); Console.WriteLine("----------------------------"); // 更新 Student studentUpd = new Student() { Id = 1, Name = "zhangsan1234", Age = 20, Sex = 2, Email = "zhangsan1234@qq.com" }; bool resultUpd = dal.Update<Student>(studentUpd) > 0 ? true : false; Console.WriteLine($"更新執(zhí)行結(jié)果:{resultUpd}"); Console.WriteLine("----------------------------"); // 刪除 bool resultDel = dal.Delete<Student>(3) > 0 ? true : false; Console.WriteLine($"刪除執(zhí)行結(jié)果:{resultDel}"); #endregion Console.ReadKey(); } } }
8、結(jié)果
9、優(yōu)化
仔細觀察上面步驟7中的代碼,你會發(fā)現(xiàn)在每個方法中都有重復(fù)的代碼,打開鏈接,執(zhí)行SqlCommand命令,那么這些重復(fù)的代碼能不能提取到一個公共的方法中進行調(diào)用呢?答案是可以的,那就是利用Func委托,看下面優(yōu)化后的代碼:
using FunApplication.AttributeExtend; using FunApplication.IDAL; using FunApplication.Model; using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace FunApplication.DAL { public class FunBaseDAL : IBaseDAL { // 數(shù)據(jù)庫鏈接字符串 private static string strConn = ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString; public int Delete<T>(int id) where T : BaseModel { Type type = typeof(T); string sql = $"delete from {type.Name} where Id=@Id"; Func<SqlCommand, int> func = (SqlCommand command) => { SqlParameter para = new SqlParameter("Id", id); command.Parameters.Add(para); return command.ExecuteNonQuery(); }; return ExcuteSql<int>(sql, func); } public int Insert<T>(T t) where T : BaseModel { int result = 0; Type type = typeof(T); var propArray = type.GetProperties().Where(p => p.Name != "Id"); string strSQL = "insert into Student Values (@Name,@Age,@Sex,@Email) "; var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray(); Func<SqlCommand, int> func = (SqlCommand command) => { command.Parameters.AddRange(parameters); return command.ExecuteNonQuery(); }; result = ExcuteSql<int>(strSQL, func); return result; } public T Query<T>(int id) where T : BaseModel { Type type = typeof(T); string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]")); string sql = $"SELECT {columnString} FROM [{type.Name}] WHERE Id=@Id"; T t = null; DataTable dt = new DataTable(); Func<SqlCommand, T> func = (SqlCommand command) => { SqlParameter para = new SqlParameter("@Id", id); command.Parameters.Add(para); SqlDataAdapter adapter = new SqlDataAdapter(command); //SqlDataReader reader = command.ExecuteReader(); //List<T> list = this.ReaderToList<T>(reader); adapter.Fill(dt); List<T> list = ConvertToList<T>(dt); T tResult = list.FirstOrDefault(); return tResult; }; t = ExcuteSql<T>(sql, func); return t; } public List<T> QueryAll<T>() where T : BaseModel { Type type = typeof(T); string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]")); string sql = $"SELECT {columnString} FROM [{type.Name}] "; T t = null; Func<SqlCommand, List<T>> func = (SqlCommand command) => { SqlDataReader reader = command.ExecuteReader(); List<T> list = this.ReaderToList<T>(reader); return list; }; return ExcuteSql<List<T>>(sql, func); } public int Update<T>(T t) where T : BaseModel { int result = 0; Type type = typeof(T); var propArray = type.GetProperties().Where(p => p.Name != "Id"); string columnString = string.Join(",", propArray.Select(p => $"[{p.GetColumnName()}]=@{p.GetColumnName()}")); var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray(); //必須參數(shù)化 否則引號? 或者值里面還有引號 string strSQL = $"UPDATE [{type.Name}] SET {columnString} WHERE Id={t.Id}"; Func<SqlCommand, int> func = (SqlCommand command) => { command.Parameters.AddRange(parameters); return command.ExecuteNonQuery(); }; result = ExcuteSql<int>(strSQL, func); return result; } //多個方法里面重復(fù)對數(shù)據(jù)庫的訪問 想通過委托解耦,去掉重復(fù)代碼 private T ExcuteSql<T>(string sql, Func<SqlCommand, T> func) { using (SqlConnection conn = new SqlConnection(strConn)) { using (SqlCommand command = new SqlCommand(sql, conn)) { conn.Open(); SqlTransaction sqlTransaction = conn.BeginTransaction(); try { command.Transaction = sqlTransaction; T tResult = func.Invoke(command); sqlTransaction.Commit(); return tResult; } catch (Exception ex) { sqlTransaction.Rollback(); throw; } } } } private List<T> ReaderToList<T>(SqlDataReader reader) where T : BaseModel { Type type = typeof(T); List<T> list = new List<T>(); while (reader.Read())//表示有數(shù)據(jù) 開始讀 { T t = (T)Activator.CreateInstance(type); foreach (var prop in type.GetProperties()) { object oValue = reader[prop.GetColumnName()]; if (oValue is DBNull) oValue = null; prop.SetValue(t, oValue);//除了guid和枚舉 } list.Add(t); } reader.Close(); return list; } } }
10、在Main()方法中調(diào)用
using FunApplication.DAL; using FunApplication.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FunApplication { class Program { static void Main(string[] args) { #region 傳統(tǒng)實現(xiàn) //BaseDAL dal = new BaseDAL(); //// 查詢 //Student student = dal.Query<Student>(2); //Console.WriteLine($"姓名:{student.Name},年齡:{student.Age},Email地址:{student.Email}"); //Console.WriteLine("----------------------------"); //// 查詢所有 //List<Student> list = dal.QueryAll<Student>(); //Console.WriteLine($"集合個數(shù):{list.Count}"); //Console.WriteLine("----------------------------"); //// 插入 //Student studentIns = new Student() //{ // Name = "小明", // Age = 20, // Sex = 2, // Email = "xiaoming@qq.com" //}; //bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false; //Console.WriteLine($"插入執(zhí)行結(jié)果:{resultIns}"); //Console.WriteLine("----------------------------"); //// 更新 //Student studentUpd = new Student() //{ // Id = 1, // Name = "zhangsan1234", // Age = 20, // Sex = 2, // Email = "zhangsan1234@qq.com" //}; //bool resultUpd = dal.Update<Student>(studentUpd) > 1 ? true : false; //Console.WriteLine($"更新執(zhí)行結(jié)果:{resultUpd}"); //Console.WriteLine("----------------------------"); //// 刪除 //bool resultDel = dal.Delete<Student>(5) > 1 ? true : false; //Console.WriteLine($"刪除執(zhí)行結(jié)果:{resultDel}"); #endregion #region 利用委托 // 查詢 FunBaseDAL dal = new FunBaseDAL(); Student student = dal.Query<Student>(1); Console.WriteLine($"姓名:{student.Name},年齡:{student.Age},Email地址:{student.Email}"); Console.WriteLine("----------------------------"); // 查詢所有 List<Student> list = dal.QueryAll<Student>(); Console.WriteLine($"集合個數(shù):{list.Count}"); Console.WriteLine("----------------------------"); // 插入 Student studentIns = new Student() { Name = "tom", Age = 19, Sex = 1, Email = "tom@163.com" }; bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false; Console.WriteLine($"插入執(zhí)行結(jié)果:{resultIns}"); Console.WriteLine("----------------------------"); List<Student> list1 = dal.QueryAll<Student>(); Console.WriteLine($"插入后集合個數(shù):{list1.Count}"); Console.WriteLine("----------------------------"); // 更新 Student studentUpd = new Student() { Id = 2, Name = "馬六123", Age = 20, Sex = 2, Email = "maliu1234@qq.com" }; bool resultUpd = dal.Update<Student>(studentUpd) > 0 ? true : false; Console.WriteLine($"更新執(zhí)行結(jié)果:{resultUpd}"); Console.WriteLine("----------------------------"); // 刪除 bool resultDel = dal.Delete<Student>(8) > 0 ? true : false; Console.WriteLine($"刪除執(zhí)行結(jié)果:{resultDel}"); List<Student> list2 = dal.QueryAll<Student>(); Console.WriteLine($"刪除后集合個數(shù):{list2.Count}"); Console.WriteLine("----------------------------"); #endregion Console.ReadKey(); } } }
11、結(jié)果
注意
在使用SqlDataReader的時候有時會報錯:“已有打開的與此Command相關(guān)聯(lián)的DataReader,必須先將它關(guān)閉”。
同時打開兩個或循環(huán)多個sqldatareader會出現(xiàn)以上錯誤。因為用的是sqldatareader做數(shù)據(jù)庫的數(shù)據(jù)讀取,sqlconnection開啟沒有關(guān)閉。
一個SqlConnection只能執(zhí)行一次事務(wù),沒用一次必須關(guān)閉然后再開啟。上面我只用了一次沒有關(guān)閉,直接開啟所以會報錯。解決方案有如下兩種:
1、其實不用多次打開在開啟,那樣實現(xiàn)起來很麻煩。直接在連接字符串的后面加上MultipleActiveResultSets=true即可。 配置文件定義如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <!--<add name="DbConnection" connectionString="Server=.;Initial Catalog=MyDb;User ID=sa;Password=123456;MultipleActiveResultSets=True"/>--> <!--配置文件里面添加MultipleActiveResultSets=True--> <add name="DbConnection" connectionString="Server=.;Initial Catalog=MyDb;User ID=sa;Password=123456;MultipleActiveResultSets=True"/> </connectionStrings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> </configuration>
2、使用DataTable
在上面是使用的SqlDataReader讀取數(shù)據(jù),然后轉(zhuǎn)換成List<T>,可以用DataTable代替SqlDataReader,這樣就不會報錯了,代碼如下:
/// <summary> /// 將DataTable轉(zhuǎn)換成List /// </summary> /// <typeparam name="T"></typeparam> /// <param name="dt"></param> /// <returns></returns> private List<T> ConvertToList<T>(DataTable dt) where T:BaseModel { Type type = typeof(T); List<T> list = new List<T>(); foreach(DataRow dr in dt.Rows) { T t = (T)Activator.CreateInstance(type); foreach(PropertyInfo prop in type.GetProperties()) { object value = dr[prop.GetColumnName()]; if(value is DBNull) { value = null; } prop.SetValue(t, value); } list.Add(t); } return list; }
到此這篇關(guān)于C#內(nèi)置泛型委托之Func委托的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#實現(xiàn)winform用子窗體刷新父窗體及子窗體改變父窗體控件值的方法
這篇文章主要介紹了C#實現(xiàn)winform用子窗體刷新父窗體及子窗體改變父窗體控件值的方法,涉及C#窗體交互的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07基于c#圖像灰度化、灰度反轉(zhuǎn)、二值化的實現(xiàn)方法詳解
本篇文章是對c#圖像灰度化、灰度反轉(zhuǎn)、二值化的實現(xiàn)方法進行了詳細的分析介紹,需要的朋友參考下2013-05-05C#實現(xiàn)讀取和設(shè)置文件與文件夾的權(quán)限
這篇文章主要為大家詳細介紹了如何使用C#實現(xiàn)讀取和設(shè)置文件與文件夾的權(quán)限,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-03-03c# 實現(xiàn)IComparable、IComparer接口、Comparer類的詳解
本篇文章是對c#中實現(xiàn)IComparable、IComparer接口、Comparer類進行了詳細的分析詳解,需要的朋友參考下2013-05-05C#統(tǒng)計C、C++及C#程序代碼行數(shù)的方法
這篇文章主要介紹了C#統(tǒng)計C、C++及C#程序代碼行數(shù)的方法,較為詳細的分析了C#統(tǒng)計文本文件的原理與相關(guān)實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08