一、什么是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<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
}`
你会发现,Func其实就是有多个输出参数并且有返回值的delegate。
3、示例
Func至少0个输入参数,至多16个输入参数,根据返回值泛型返回。必须有返回值,不可void。
Func<int> 表示没有输入参参,返回值为int类型的委托。
Func<object,string,int> 表示传入参数为object, string ,返回值为int类型的委托。
Func<object,string,int> 表示传入参数为object, string, 返回值为int类型的委托。
Func<T1,T2,,T3,int> 表示传入参数为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)
{
// 无参数,只要返回值
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(“——————————————“);
//有一个参数,一个返回值
Func<int, int> fun4 = new Func<int, int>(FunWithPara);
int result4 = fun4(4);
Console.WriteLine($”这里是一个参数一个返回值的方法,返回值是:{result4}”);
Console.WriteLine(“——————————————“);
// 使用委托
Func<int, string> fun5 = delegate (int i) { return i.ToString(); };
string result5 = fun5(5);
Console.WriteLine($”这里是一个参数一个返回值的委托,返回值是:{result5}”);
Console.WriteLine(“——————————————“);
// 使用匿名委托
Func<int, string> fun6 = (int i) =>
{
return i.ToString();
};
string result6 = fun6(6);
Console.WriteLine($”这里是一个参数一个返回值的匿名委托,返回值是:{result6}”);
Console.WriteLine(“——————————————“);
// 多个输入参数
Func<int, string, bool> fun7 = new Func<int, string, bool>(FunWithMultiPara);
bool result7 = fun7(2, “2”);
Console.WriteLine($”这里是有多个输入参数的方法,返回值是:{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($”这里是有多个输入参数的委托,返回值是:{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($”这里是有多个输入参数的匿名委托,返回值是:{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;}}
}`
运行结果:
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<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、定义数据库方法接口实现类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
{
// 数据库链接字符串
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();//必须参数化 否则引号? 或者值里面还有引号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())//表示有数据 开始读{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()方法中调用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<Student>(2);Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}");Console.WriteLine("----------------------------");// 查询所有List<Student> list = dal.QueryAll<Student>();Console.WriteLine($"集合个数:{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($"插入执行结果:{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($"更新执行结果:{resultUpd}");Console.WriteLine("----------------------------");// 删除bool resultDel = dal.Delete<Student>(3) > 0 ? true : false;Console.WriteLine($"删除执行结果:{resultDel}");#endregionConsole.ReadKey();}}
}8、结果

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<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();//必须参数化 否则引号? 或者值里面还有引号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;}//多个方法里面重复对数据库的访问 想通过委托解耦,去掉重复代码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())//表示有数据 开始读{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()方法中调用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<Student>(2);//Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}");//Console.WriteLine("----------------------------");//// 查询所有//List<Student> list = dal.QueryAll<Student>();//Console.WriteLine($"集合个数:{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($"插入执行结果:{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($"更新执行结果:{resultUpd}");//Console.WriteLine("----------------------------");//// 删除//bool resultDel = dal.Delete<Student>(5) > 1 ? true : false;//Console.WriteLine($"删除执行结果:{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($"集合个数:{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($"插入执行结果:{resultIns}");Console.WriteLine("----------------------------");List<Student> list1 = dal.QueryAll<Student>();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<Student>(studentUpd) > 0 ? true : false;Console.WriteLine($"更新执行结果:{resultUpd}");Console.WriteLine("----------------------------");// 删除bool resultDel = dal.Delete<Student>(8) > 0 ? true : false;Console.WriteLine($"删除执行结果:{resultDel}");List<Student> list2 = dal.QueryAll<Student>();Console.WriteLine($"删除后集合个数:{list2.Count}");Console.WriteLine("----------------------------");#endregionConsole.ReadKey();}}
}`
11、结果
注意
在使用SqlDataReader的时候有时会报错:“已有打开的与此Command相关联的DataReader,必须先将它关闭”。
同时打开两个或循环多个sqldatareader会出现以上错误。因为用的是sqldatareader做数据库的数据读取,sqlconnection开启没有关闭。
一个SqlConnection只能执行一次事务,没用一次必须关闭然后再开启。上面我只用了一次没有关闭,直接开启所以会报错。解决方案有如下两种:
1、其实不用多次打开在开启,那样实现起来很麻烦。直接在连接字符串的后面加上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读取数据,然后转换成List<T>,可以用DataTable代替SqlDataReader,这样就不会报错了,代码如下:/// <summary>
/// 将DataTable转换成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;
}`
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号