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<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 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($"集合个数:{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}");
          #endregion
          Console.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("----------------------------");
          #endregion
          Console.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;
}

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

可扩展标记语言(XML)文件是一种标准的文本文件,它使用特定的标记来描述文档的结构以及其他特性。通过将XML转换为PDF,能够便于文件传输及共享。本文,将介绍通过C#及VB.NET代码来实现该格式转 ...