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

2022-07-16,

一、什么是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委托的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。

《C#内置泛型委托之Func委托.doc》

下载本文的Word格式文档,以方便收藏与打印。