ASCII码 ASCII码

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

发布于:2022-03-14 10:39:28  栏目:技术文档

一、什么是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();

  1. }
  2. static int FunWithNoPara()
  3. {
  4. return 10;
  5. }
  6. static int FunWithPara(int i)
  7. {
  8. return i;
  9. }
  10. static bool FunWithMultiPara(int i,string s)
  11. {
  12. return i.ToString().Equals(s) ? true : false;
  13. }
  14. }

}`运行结果: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; }

  1. public int Age { get; set; }
  2. public int Sex { get; set; }
  3. public string Email { get; set; }
  4. }

}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;

  1. List<T> QueryAll<T>() where T : BaseModel;
  2. int Insert<T>(T t) where T : BaseModel;
  3. int Update<T>(T t) where T : BaseModel;
  4. int Delete<T>(int id) where T : BaseModel;
  5. }

}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; }

  1. private string _Name = null;
  2. public string GetColumnName()
  3. {
  4. return this._Name;
  5. }
  6. }

}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;

  1. using (SqlConnection conn = new SqlConnection(strConn))
  2. {
  3. string strSQL = "delete from Student where Id=@Id";
  4. SqlParameter para = new SqlParameter("Id", id);
  5. SqlCommand command = new SqlCommand(strSQL, conn);
  6. command.Parameters.Add(para);
  7. conn.Open();
  8. result = command.ExecuteNonQuery();
  9. }
  10. return result;
  11. }
  12. public int Insert<T>(T t) where T : BaseModel
  13. {
  14. int result = 0;
  15. using (SqlConnection conn = new SqlConnection(strConn))
  16. {
  17. Type type = typeof(T);
  18. var propArray = type.GetProperties().Where(p => p.Name != "Id");
  19. string strSQL = "insert into Student Values (@Name,@Age,@Sex,@Email) ";
  20. SqlCommand command = new SqlCommand(strSQL, conn);
  21. var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();
  22. command.Parameters.AddRange(parameters);
  23. conn.Open();
  24. result = command.ExecuteNonQuery();
  25. }
  26. return result;
  27. }
  28. public T Query<T>(int id) where T : BaseModel
  29. {
  30. Type type = typeof(T);
  31. string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]"));
  32. string sql = $"SELECT {columnString} FROM [{type.Name}] WHERE Id={id}";
  33. T t = null;// (T)Activator.CreateInstance(type);
  34. using (SqlConnection conn = new SqlConnection(strConn))
  35. {
  36. SqlCommand command = new SqlCommand(sql, conn);
  37. conn.Open();
  38. SqlDataReader reader = command.ExecuteReader();
  39. List<T> list = this.ReaderToList<T>(reader);
  40. t = list.FirstOrDefault();
  41. }
  42. return t;
  43. }
  44. public List<T> QueryAll<T>() where T : BaseModel
  45. {
  46. Type type = typeof(T);
  47. string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]"));
  48. string sql = $"SELECT {columnString} FROM [{type.Name}] ";
  49. List<T> list = new List<T>();
  50. using (SqlConnection conn = new SqlConnection(strConn))
  51. {
  52. SqlCommand command = new SqlCommand(sql, conn);
  53. conn.Open();
  54. SqlDataReader reader = command.ExecuteReader();
  55. list = this.ReaderToList<T>(reader);
  56. }
  57. return list;
  58. }
  59. public int Update<T>(T t) where T : BaseModel
  60. {
  61. int result = 0;
  62. using (SqlConnection conn = new SqlConnection(strConn))
  63. {
  64. Type type = typeof(T);
  65. var propArray = type.GetProperties().Where(p => p.Name != "Id");
  66. string columnString = string.Join(",", propArray.Select(p => $"[{p.GetColumnName()}]=@{p.GetColumnName()}"));
  67. var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();
  68. //必须参数化 否则引号? 或者值里面还有引号
  69. string strSQL = $"UPDATE [{type.Name}] SET {columnString} WHERE Id={t.Id}";
  70. SqlCommand command = new SqlCommand(strSQL, conn);
  71. command.Parameters.AddRange(parameters);
  72. conn.Open();
  73. result = command.ExecuteNonQuery();
  74. }
  75. return result;
  76. }
  77. private List<T> ReaderToList<T>(SqlDataReader reader) where T : BaseModel
  78. {
  79. Type type = typeof(T);
  80. List<T> list = new List<T>();
  81. while (reader.Read())//表示有数据 开始读
  82. {
  83. T t = (T)Activator.CreateInstance(type);
  84. foreach (var prop in type.GetProperties())
  85. {
  86. object oValue = reader[prop.GetColumnName()];
  87. if (oValue is DBNull)
  88. oValue = null;
  89. prop.SetValue(t, oValue);//除了guid和枚举
  90. }
  91. list.Add(t);
  92. }
  93. return list;
  94. }
  95. }

}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) {

  1. #region MyRegion
  2. BaseDAL dal = new BaseDAL();
  3. // 查询
  4. Student student = dal.Query<Student>(2);
  5. Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}");
  6. Console.WriteLine("----------------------------");
  7. // 查询所有
  8. List<Student> list = dal.QueryAll<Student>();
  9. Console.WriteLine($"集合个数:{list.Count}");
  10. Console.WriteLine("----------------------------");
  11. // 插入
  12. Student studentIns = new Student()
  13. {
  14. Name = "小明",
  15. Age = 20,
  16. Sex = 2,
  17. Email = "xiaoming@qq.com"
  18. };
  19. bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false;
  20. Console.WriteLine($"插入执行结果:{resultIns}");
  21. Console.WriteLine("----------------------------");
  22. // 更新
  23. Student studentUpd = new Student()
  24. {
  25. Id = 1,
  26. Name = "zhangsan1234",
  27. Age = 20,
  28. Sex = 2,
  29. Email = "zhangsan1234@qq.com"
  30. };
  31. bool resultUpd = dal.Update<Student>(studentUpd) > 0 ? true : false;
  32. Console.WriteLine($"更新执行结果:{resultUpd}");
  33. Console.WriteLine("----------------------------");
  34. // 删除
  35. bool resultDel = dal.Delete<Student>(3) > 0 ? true : false;
  36. Console.WriteLine($"删除执行结果:{resultDel}");
  37. #endregion
  38. Console.ReadKey();
  39. }
  40. }

}8、结果 ![](https://img.php.cn/upload/image/780/909/496/1647168486223716.jpg) 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;

  1. public int Delete<T>(int id) where T : BaseModel
  2. {
  3. Type type = typeof(T);
  4. string sql = $"delete from {type.Name} where Id=@Id";
  5. Func<SqlCommand, int> func = (SqlCommand command) =>
  6. {
  7. SqlParameter para = new SqlParameter("Id", id);
  8. command.Parameters.Add(para);
  9. return command.ExecuteNonQuery();
  10. };
  11. return ExcuteSql<int>(sql, func);
  12. }
  13. public int Insert<T>(T t) where T : BaseModel
  14. {
  15. int result = 0;
  16. Type type = typeof(T);
  17. var propArray = type.GetProperties().Where(p => p.Name != "Id");
  18. string strSQL = "insert into Student Values (@Name,@Age,@Sex,@Email) ";
  19. var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();
  20. Func<SqlCommand, int> func = (SqlCommand command) =>
  21. {
  22. command.Parameters.AddRange(parameters);
  23. return command.ExecuteNonQuery();
  24. };
  25. result = ExcuteSql<int>(strSQL, func);
  26. return result;
  27. }
  28. public T Query<T>(int id) where T : BaseModel
  29. {
  30. Type type = typeof(T);
  31. string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]"));
  32. string sql = $"SELECT {columnString} FROM [{type.Name}] WHERE Id=@Id";
  33. T t = null;
  34. DataTable dt = new DataTable();
  35. Func<SqlCommand, T> func = (SqlCommand command) =>
  36. {
  37. SqlParameter para = new SqlParameter("@Id", id);
  38. command.Parameters.Add(para);
  39. SqlDataAdapter adapter = new SqlDataAdapter(command);
  40. //SqlDataReader reader = command.ExecuteReader();
  41. //List<T> list = this.ReaderToList<T>(reader);
  42. adapter.Fill(dt);
  43. List<T> list = ConvertToList<T>(dt);
  44. T tResult = list.FirstOrDefault();
  45. return tResult;
  46. };
  47. t = ExcuteSql<T>(sql, func);
  48. return t;
  49. }
  50. public List<T> QueryAll<T>() where T : BaseModel
  51. {
  52. Type type = typeof(T);
  53. string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]"));
  54. string sql = $"SELECT {columnString} FROM [{type.Name}] ";
  55. T t = null;
  56. Func<SqlCommand, List<T>> func = (SqlCommand command) =>
  57. {
  58. SqlDataReader reader = command.ExecuteReader();
  59. List<T> list = this.ReaderToList<T>(reader);
  60. return list;
  61. };
  62. return ExcuteSql<List<T>>(sql, func);
  63. }
  64. public int Update<T>(T t) where T : BaseModel
  65. {
  66. int result = 0;
  67. Type type = typeof(T);
  68. var propArray = type.GetProperties().Where(p => p.Name != "Id");
  69. string columnString = string.Join(",", propArray.Select(p => $"[{p.GetColumnName()}]=@{p.GetColumnName()}"));
  70. var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();
  71. //必须参数化 否则引号? 或者值里面还有引号
  72. string strSQL = $"UPDATE [{type.Name}] SET {columnString} WHERE Id={t.Id}";
  73. Func<SqlCommand, int> func = (SqlCommand command) =>
  74. {
  75. command.Parameters.AddRange(parameters);
  76. return command.ExecuteNonQuery();
  77. };
  78. result = ExcuteSql<int>(strSQL, func);
  79. return result;
  80. }
  81. //多个方法里面重复对数据库的访问 想通过委托解耦,去掉重复代码
  82. private T ExcuteSql<T>(string sql, Func<SqlCommand, T> func)
  83. {
  84. using (SqlConnection conn = new SqlConnection(strConn))
  85. {
  86. using (SqlCommand command = new SqlCommand(sql, conn))
  87. {
  88. conn.Open();
  89. SqlTransaction sqlTransaction = conn.BeginTransaction();
  90. try
  91. {
  92. command.Transaction = sqlTransaction;
  93. T tResult = func.Invoke(command);
  94. sqlTransaction.Commit();
  95. return tResult;
  96. }
  97. catch (Exception ex)
  98. {
  99. sqlTransaction.Rollback();
  100. throw;
  101. }
  102. }
  103. }
  104. }
  105. private List<T> ReaderToList<T>(SqlDataReader reader) where T : BaseModel
  106. {
  107. Type type = typeof(T);
  108. List<T> list = new List<T>();
  109. while (reader.Read())//表示有数据 开始读
  110. {
  111. T t = (T)Activator.CreateInstance(type);
  112. foreach (var prop in type.GetProperties())
  113. {
  114. object oValue = reader[prop.GetColumnName()];
  115. if (oValue is DBNull)
  116. oValue = null;
  117. prop.SetValue(t, oValue);//除了guid和枚举
  118. }
  119. list.Add(t);
  120. }
  121. reader.Close();
  122. return list;
  123. }
  124. }

}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) {

  1. #region 传统实现
  2. //BaseDAL dal = new BaseDAL();
  3. //// 查询
  4. //Student student = dal.Query<Student>(2);
  5. //Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}");
  6. //Console.WriteLine("----------------------------");
  7. //// 查询所有
  8. //List<Student> list = dal.QueryAll<Student>();
  9. //Console.WriteLine($"集合个数:{list.Count}");
  10. //Console.WriteLine("----------------------------");
  11. //// 插入
  12. //Student studentIns = new Student()
  13. //{
  14. // Name = "小明",
  15. // Age = 20,
  16. // Sex = 2,
  17. // Email = "xiaoming@qq.com"
  18. //};
  19. //bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false;
  20. //Console.WriteLine($"插入执行结果:{resultIns}");
  21. //Console.WriteLine("----------------------------");
  22. //// 更新
  23. //Student studentUpd = new Student()
  24. //{
  25. // Id = 1,
  26. // Name = "zhangsan1234",
  27. // Age = 20,
  28. // Sex = 2,
  29. // Email = "zhangsan1234@qq.com"
  30. //};
  31. //bool resultUpd = dal.Update<Student>(studentUpd) > 1 ? true : false;
  32. //Console.WriteLine($"更新执行结果:{resultUpd}");
  33. //Console.WriteLine("----------------------------");
  34. //// 删除
  35. //bool resultDel = dal.Delete<Student>(5) > 1 ? true : false;
  36. //Console.WriteLine($"删除执行结果:{resultDel}");
  37. #endregion
  38. #region 利用委托
  39. // 查询
  40. FunBaseDAL dal = new FunBaseDAL();
  41. Student student = dal.Query<Student>(1);
  42. Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}");
  43. Console.WriteLine("----------------------------");
  44. // 查询所有
  45. List<Student> list = dal.QueryAll<Student>();
  46. Console.WriteLine($"集合个数:{list.Count}");
  47. Console.WriteLine("----------------------------");
  48. // 插入
  49. Student studentIns = new Student()
  50. {
  51. Name = "tom",
  52. Age = 19,
  53. Sex = 1,
  54. Email = "tom@163.com"
  55. };
  56. bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false;
  57. Console.WriteLine($"插入执行结果:{resultIns}");
  58. Console.WriteLine("----------------------------");
  59. List<Student> list1 = dal.QueryAll<Student>();
  60. Console.WriteLine($"插入后集合个数:{list1.Count}");
  61. Console.WriteLine("----------------------------");
  62. // 更新
  63. Student studentUpd = new Student()
  64. {
  65. Id = 2,
  66. Name = "马六123",
  67. Age = 20,
  68. Sex = 2,
  69. Email = "maliu1234@qq.com"
  70. };
  71. bool resultUpd = dal.Update<Student>(studentUpd) > 0 ? true : false;
  72. Console.WriteLine($"更新执行结果:{resultUpd}");
  73. Console.WriteLine("----------------------------");
  74. // 删除
  75. bool resultDel = dal.Delete<Student>(8) > 0 ? true : false;
  76. Console.WriteLine($"删除执行结果:{resultDel}");
  77. List<Student> list2 = dal.QueryAll<Student>();
  78. Console.WriteLine($"删除后集合个数:{list2.Count}");
  79. Console.WriteLine("----------------------------");
  80. #endregion
  81. Console.ReadKey();
  82. }
  83. }

}`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;}`

相关推荐
阅读 +