C#内置泛型委托之Func委托

一、什么是Func委托 Func委托代表有返回类型的委托
二、Func委托定义 查看Func的定义:

using System.Runtime.CompilerServices; namespace System{//// 摘要://封装一个方法,该方法具有两个参数,并返回由 TResult 参数指定的类型的值。//// 参数://arg1://此委托封装的方法的第一个参数。////arg2://此委托封装的方法的第二个参数。//// 类型参数://T1://此委托封装的方法的第一个参数的类型。////T2://此委托封装的方法的第二个参数的类型。////TResult://此委托封装的方法的返回值类型。//// 返回结果://此委托封装的方法的返回值。[TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]public delegate TResult Func(T1 arg1, T2 arg2); }

你会发现,Func其实就是有多个输出参数并且有返回值的delegate。
3、示例 Func至少0个输入参数,至多16个输入参数,根据返回值泛型返回。必须有返回值,不可void。
Func 表示没有输入参参,返回值为int类型的委托。
Func 表示传入参数为object, string ,返回值为int类型的委托。
Func 表示传入参数为object, string, 返回值为int类型的委托。
Func 表示传入参数为T1,T2,,T3(泛型),返回值为int类型的委托。
C#内置泛型委托之Func委托
文章图片

代码示例如下:
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){// 无参数,只要返回值 Func fun1 = new Func(FunWithNoPara); int result1= fun1(); Console.WriteLine(result1); Console.WriteLine("----------------------------"); Func fun2 = delegate { return 19; }; int result2 = fun2(); Console.WriteLine(result2); Console.WriteLine("----------------------------"); Func fun3 = () => { return 3; }; int result3 = fun3(); Console.WriteLine(result3); Console.WriteLine("----------------------------"); //有一个参数,一个返回值Func fun4 = new Func(FunWithPara); int result4 = fun4(4); Console.WriteLine($"这里是一个参数一个返回值的方法,返回值是:{result4}"); Console.WriteLine("----------------------------"); // 使用委托Func fun5 = delegate (int i) { return i.ToString(); }; string result5 = fun5(5); Console.WriteLine($"这里是一个参数一个返回值的委托,返回值是:{result5}"); Console.WriteLine("----------------------------"); // 使用匿名委托Func fun6 = (int i) => {return i.ToString(); }; string result6 = fun6(6); Console.WriteLine($"这里是一个参数一个返回值的匿名委托,返回值是:{result6}"); Console.WriteLine("----------------------------"); // 多个输入参数Func fun7 = new Func(FunWithMultiPara); bool result7 = fun7(2, "2"); Console.WriteLine($"这里是有多个输入参数的方法,返回值是:{result7}"); Console.WriteLine("----------------------------"); // 使用委托Func fun8 = delegate (int i, string s) {return i.ToString().Equals(s) ? true : false; }; bool result8 = fun8(2, "abc"); Console.WriteLine($"这里是有多个输入参数的委托,返回值是:{result8}"); Console.WriteLine("----------------------------"); // 使用匿名委托Func fun9 = (int i, string s) => {return i.ToString().Equals(s) ? true : false; }; bool result9 = fun9(45, "ert"); Console.WriteLine($"这里是有多个输入参数的匿名委托,返回值是:{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; }}}

运行结果:
C#内置泛型委托之Func委托
文章图片

4、真实示例 在下面的示例中,利用Func委托封装数据库通用访问类。
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、定义数据库访问方法接口
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(int id) where T : BaseModel; List QueryAll() where T : BaseModel; int Insert(T t) where T : BaseModel; int Update(T t) where T : BaseModel; int Delete(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、定义数据库方法接口实现类
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{publicclass BaseDAL : IBaseDAL{// 数据库链接字符串private static string strConn = ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString; publicint Delete(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) 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(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 list = this.ReaderToList(reader); t = list.FirstOrDefault(); }return t; }public List QueryAll() 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 list = new List(); using (SqlConnection conn = new SqlConnection(strConn)){SqlCommand command = new SqlCommand(sql, conn); conn.Open(); SqlDataReader reader = command.ExecuteReader(); list = this.ReaderToList(reader); }return list; }public int Update(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(); //必须参数化否则引号?或者值里面还有引号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 ReaderToList(SqlDataReader reader) where T : BaseModel{Type type = typeof(T); List list = new List(); while (reader.Read())//表示有数据开始读{T t = (T)Activator.CreateInstance(type); foreach (var prop in type.GetProperties()){object oValue = https://www.it610.com/article/reader[prop.GetColumnName()]; if (oValue is DBNull)oValue = null; prop.SetValue(t, oValue); //除了guid和枚举}list.Add(t); }return list; }}}

7、在Main()方法中调用
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 MyRegionBaseDAL dal = new BaseDAL(); // 查询Student student = dal.Query(2); Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}"); Console.WriteLine("----------------------------"); // 查询所有List list = dal.QueryAll(); Console.WriteLine($"集合个数:{list.Count}"); Console.WriteLine("----------------------------"); // 插入Student studentIns = new Student(){Name = "小明",Age = 20,Sex = 2,Email = "xiaoming@qq.com"}; bool resultIns = dal.Insert(studentIns) > 0 ? true : false; Console.WriteLine($"插入执行结果:{resultIns}"); Console.WriteLine("----------------------------"); // 更新Student studentUpd = new Student(){Id = 1,Name = "zhangsan1234",Age = 20,Sex = 2,Email = "zhangsan1234@qq.com"}; bool resultUpd = dal.Update(studentUpd) > 0 ? true : false; Console.WriteLine($"更新执行结果:{resultUpd}"); Console.WriteLine("----------------------------"); // 删除bool resultDel = dal.Delete(3) > 0 ? true : false; Console.WriteLine($"删除执行结果:{resultDel}"); #endregionConsole.ReadKey(); }}}

8、结果
C#内置泛型委托之Func委托
文章图片

9、优化
仔细观察上面步骤7中的代码,你会发现在每个方法中都有重复的代码,打开链接,执行SqlCommand命令,那么这些重复的代码能不能提取到一个公共的方法中进行调用呢?答案是可以的,那就是利用Func委托,看下面优化后的代码:
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{// 数据库链接字符串private static string strConn = ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString; public int Delete(int id) where T : BaseModel{Type type = typeof(T); string sql = $"delete from {type.Name} where Id=@Id"; Func func = (SqlCommand command) => {SqlParameter para = new SqlParameter("Id", id); command.Parameters.Add(para); return command.ExecuteNonQuery(); }; return ExcuteSql(sql, func); }public int Insert(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 func = (SqlCommand command) => {command.Parameters.AddRange(parameters); return command.ExecuteNonQuery(); }; result = ExcuteSql(strSQL, func); return result; }public T Query(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 func = (SqlCommand command) => {SqlParameter para = new SqlParameter("@Id", id); command.Parameters.Add(para); SqlDataAdapter adapter = new SqlDataAdapter(command); //SqlDataReader reader = command.ExecuteReader(); //List list = this.ReaderToList(reader); adapter.Fill(dt); List list = ConvertToList(dt); T tResult = list.FirstOrDefault(); return tResult; }; t = ExcuteSql(sql, func); return t; }public List QueryAll() 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> func = (SqlCommand command) =>{SqlDataReader reader = command.ExecuteReader(); List list = this.ReaderToList(reader); return list; }; return ExcuteSql>(sql, func); }public int Update(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(); //必须参数化否则引号?或者值里面还有引号string strSQL = $"UPDATE [{type.Name}] SET {columnString} WHERE Id={t.Id}"; Func func = (SqlCommand command) => {command.Parameters.AddRange(parameters); return command.ExecuteNonQuery(); }; result = ExcuteSql(strSQL, func); return result; }//多个方法里面重复对数据库的访问想通过委托解耦,去掉重复代码private T ExcuteSql(string sql, Func 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 ReaderToList(SqlDataReader reader) where T : BaseModel{Type type = typeof(T); List list = new List(); while (reader.Read())//表示有数据开始读{T t = (T)Activator.CreateInstance(type); foreach (var prop in type.GetProperties()){object oValue = https://www.it610.com/article/reader[prop.GetColumnName()]; if (oValue is DBNull)oValue = null; prop.SetValue(t, oValue); //除了guid和枚举}list.Add(t); }reader.Close(); return list; }}}

10、在Main()方法中调用
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 传统实现//BaseDAL dal = new BaseDAL(); //// 查询//Student student = dal.Query(2); //Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}"); //Console.WriteLine("----------------------------"); //// 查询所有//List list = dal.QueryAll(); //Console.WriteLine($"集合个数:{list.Count}"); //Console.WriteLine("----------------------------"); //// 插入//Student studentIns = new Student()//{//Name = "小明",//Age = 20,//Sex = 2,//Email = "xiaoming@qq.com"//}; //bool resultIns = dal.Insert(studentIns) > 0 ? true : false; //Console.WriteLine($"插入执行结果:{resultIns}"); //Console.WriteLine("----------------------------"); //// 更新//Student studentUpd = new Student()//{//Id = 1,//Name = "zhangsan1234",//Age = 20,//Sex = 2,//Email = "zhangsan1234@qq.com"//}; //bool resultUpd = dal.Update(studentUpd) > 1 ? true : false; //Console.WriteLine($"更新执行结果:{resultUpd}"); //Console.WriteLine("----------------------------"); //// 删除//bool resultDel = dal.Delete(5) > 1 ? true : false; //Console.WriteLine($"删除执行结果:{resultDel}"); #endregion#region 利用委托// 查询FunBaseDAL dal = new FunBaseDAL(); Student student = dal.Query(1); Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}"); Console.WriteLine("----------------------------"); // 查询所有List list = dal.QueryAll(); Console.WriteLine($"集合个数:{list.Count}"); Console.WriteLine("----------------------------"); // 插入Student studentIns = new Student(){Name = "tom",Age = 19,Sex = 1,Email = "tom@163.com"}; bool resultIns = dal.Insert(studentIns) > 0 ? true : false; Console.WriteLine($"插入执行结果:{resultIns}"); Console.WriteLine("----------------------------"); List list1 = dal.QueryAll(); Console.WriteLine($"插入后集合个数:{list1.Count}"); Console.WriteLine("----------------------------"); // 更新Student studentUpd = new Student(){Id = 2,Name = "马六123",Age = 20,Sex = 2,Email = "maliu1234@qq.com"}; bool resultUpd = dal.Update(studentUpd) > 0 ? true : false; Console.WriteLine($"更新执行结果:{resultUpd}"); Console.WriteLine("----------------------------"); // 删除bool resultDel = dal.Delete(8) > 0 ? true : false; Console.WriteLine($"删除执行结果:{resultDel}"); List list2 = dal.QueryAll(); Console.WriteLine($"删除后集合个数:{list2.Count}"); Console.WriteLine("----------------------------"); #endregionConsole.ReadKey(); }}}

11、结果
C#内置泛型委托之Func委托
文章图片

注意
在使用SqlDataReader的时候有时会报错:“已有打开的与此Command相关联的DataReader,必须先将它关闭”。
同时打开两个或循环多个sqldatareader会出现以上错误。因为用的是sqldatareader做数据库的数据读取,sqlconnection开启没有关闭。
一个SqlConnection只能执行一次事务,没用一次必须关闭然后再开启。上面我只用了一次没有关闭,直接开启所以会报错。解决方案有如下两种:
1、其实不用多次打开在开启,那样实现起来很麻烦。直接在连接字符串的后面加上MultipleActiveResultSets=true即可。 配置文件定义如下:

2、使用DataTable 在上面是使用的SqlDataReader读取数据,然后转换成List,可以用DataTable代替SqlDataReader,这样就不会报错了,代码如下:
/// /// 将DataTable转换成List/// /// /// /// private List ConvertToList(DataTable dt) where T:BaseModel{Type type = typeof(T); List list = new List(); foreach(DataRow dr in dt.Rows){T t = (T)Activator.CreateInstance(type); foreach(PropertyInfo prop in type.GetProperties()){object value = https://www.it610.com/article/dr[prop.GetColumnName()]; if(value is DBNull){value = null; }prop.SetValue(t, value); }list.Add(t); }return list; }

【C#内置泛型委托之Func委托】到此这篇关于C#内置泛型委托之Func委托的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    推荐阅读