剑轩

C#委托与lamdba表达式入门,以及Lamdba自己实现SelectMany等

电脑版发表于:2019/9/29 11:17


 ?委托:

l  什么委托:

            本质是方法指针

l  委托的作用:

   作用:进行方法传递,方法可以当作一个参数进行传递,C#里边要传递方法需要借助委托

l  使用委托一般的步骤:

         1:定义委托

              public delegate 返回值 方法名称(参数);

         2:委托可以关联方法

         3:执行委托,就相当于执行方法

  • public static void Way()
    {
        Console.WriteLine("hello delegate");
    }
    //1:定义一个无参数无返回值的委托
    public delegate void DEL_Way();
    static void Main(string[] args)
    {
        //2:给委托关联一个方法
        DEL_Way delway = Way;
        //3:执行委托
        delway.Invoke();
        Console.ReadLine();
    }

     

  l  委托实现一个简单的定时器:

public class MyTimer
{
    public void Start(Action way, int timespan)
    {
        while (true)
        {
            way();
            System.Threading.Thread.Sleep(timespan);
        }
    }
}

        执行:

MyTimer mytimer = new MyTimer();
mytimer.Start(Way, 1000);

 

 ?系统委托

  作用:简化委托的定义,系统已经定义好,使用的时候直接使用,不需要重复定义

 

l  系统委托的主要分类

   Action:无返回值,有0-16个参数

  Func:有返回值,有0-16个参数

          Predicate:固定返回值为bool

          Comparison:固定返回值为int

          其实后面两个完全可以使用func替换,但是为了一目了然一看就知道返回值是什么

      l  实例

Action action = Way;
action();
Action<int> action2 = Square;
action2(6);
Func<int, int, int> func = Add;
int result = func(8, 8);
Console.WriteLine(result);


?Lamdba表达式

 l  作用:

lamdba表达式可以简化方法的定义

 l  语法: 

   (方法参数)=>{方法体};

   例如:无参无返回值的lamdba表达式

    ()=>{};

       又如:一个参数无返回值

                 (a)=>{};

       在如:一个参数一个返回值

                 (a)=>{return a*a};

       还如:2个参数1个返回值             

                 (a,b)=>{return a+b};

    l  实例:

//无参无返回值的lamdba表达式
Action action = () => { Console.WriteLine("hello lamdba"); };
action();

//一个参数无返回值
Action<int> action2 = (a) => { Console.WriteLine(a * a); };
action2(6);

//一个参数一个返回值
Func<int, int> func = (a) => { return a * a; };
int square = func(8);
Console.WriteLine(square);

//2个参数1个返回值
Func<int, int, int> funcsum = (a, b) => { return a + b; };
int result = funcsum(3, 6);
Console.WriteLine(result);

    执行定时器的就可以用lamdba来简化方法:

MyTimer mytime = new MyTimer();
mytime.Start(() =>
{
    Console.WriteLine("国庆快乐");
}, 1000);

    做一点简单的集合查询:

static void Main(string[] args)
{
    List<string> fruitList = new List<string>() { "葡萄", "香蕉", "西瓜", "木瓜", "哈密瓜" };
    Console.WriteLine("请输入查询名字");
    string fruitName = Console.ReadLine();
    var result = fruitList.Where(a => a.Contains(fruitName));
    Console.ReadLine();
}

     对象集合lamdba查询:

static void Main(string[] args)
{
    List<Users> ulist = new List<Users>() {
        new Users() {username="李清照", age=23, address="宋" },
        new Users() {username="黄月英", age=18, address="三国" },
        new Users() {username="黄忠", age=109, address="三国" },
        new Users() {username="黄蓉", age=27, address="射雕" }
    };
    Console.WriteLine("请输入查询姓名");
    string name = Console.ReadLine();
    var result = ulist.Where(a => a.username.Contains(name)).OrderByDescending(a => a.age).ToList();
    Console.ReadLine();
}


?Lamdba表达式自己实现foreach,Where,FirstOrDefault,SelectMany

自己实现一下微软提供的集合查询也算是抛砖引玉了

public static class IEnumerableExtands
{
    public static void MyForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (T item in source)
        {
            action(item);
        }
    }
    //根据条件查询
    public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> source, Predicate<T> action)
    {
        List<T> list = new List<T>();
        foreach (T item in source)
        {
            if (action(item))
            {
                list.Add(item);
            }
        }
        return list;
    }
    //根据条件查询返回第一条或者默认值
    public static T MyFirstOrDefault<T>(this IEnumerable<T> source, Predicate<T> action)
    {
        T val = default(T);
        foreach (T item in source)
        {
            if (action(item))
            {
                return item;
            }
        }
        return val;
    }
}

效果:

实现集合查询SelectMany

自己写一下里边的原理对了解它的用法有很大的帮助,select都作用类似就是把1对多的格式处理成1对1的格式,

比如这里的把UserScoreViewModel格式处理成UserScoreViewModel2

我们要想写通用的扩展方法出来可以先写一个专门的处理UserScoreViewModel格式成UserScoreViewModel2的方法,这样做也便于我们分析和理解问题,特别是如果我们要进行授课,这种方式更能够让学生理解,一步一步的深入。

其实很简单就是两个遍历就好

/// <summary>
/// 把List<UserScoreViewModel>转换成List<UserScoreViewModel2>格式
/// </summary>
public List<UserScoreViewModel2> Parse(List<UserScoreViewModel> source)
{
    List<UserScoreViewModel2> result = new List<UserScoreViewModel2>();
    foreach (UserScoreViewModel item in source)
    {
        foreach (Score scoreItem in item.scoreList)
        {
            UserScoreViewModel2 uscore = new UserScoreViewModel2();
            uscore.UserName = item.UserName;
            uscore.score = scoreItem;
            result.Add(uscore);
        }
    }
    return result;
}

然后我们在去写通用的:

public static class IEnumberExtands
{
    /// <summary>
    /// 自己实现MySelectMany
    /// </summary>
    /// <typeparam name="TSource">对应外面的集合源类型</typeparam>
    /// <typeparam name="TCollection">集合里边子集合类型</typeparam>
    /// <typeparam name="TResult">返回值类型</typeparam>
    /// <returns></returns>
    public static IEnumerable<TResult> MySelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
    {
        List<TResult> resultList = new List<TResult>();
        foreach (TSource tsource in source)
        {
            //通过外面传递的lamdba拿到集合
            IEnumerable<TCollection> sublist = collectionSelector(tsource);
            foreach (TCollection item in sublist)
            {
                //通过外面传递的lamdba拿到返回对象
                TResult result = resultSelector(tsource, item);
                resultList.Add(result);
            }
        }
        return resultList;
    }
}

上面的方法没有处理为空的情况,如果外面写了DefaultIfEmpty()就容易报错,所以我们可以使用反射创建一个默认对象处理一下这种情况,不然我们就只有在外面去处理了

public static IEnumerable<TResult> MySelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
{
    List<TResult> userScoreViewModel2List = new List<TResult>();
    foreach (TSource item in source)
    {
        foreach (TCollection score in collectionSelector(item))
        {
            if (score == null)
            {
                //使用反射创建对象,避免为空报错,为空的时候我们就给一个默认值
                TCollection collobj = Activator.CreateInstance<TCollection>();
                TResult tresult = resultSelector(item, collobj);
                userScoreViewModel2List.Add(tresult);
            }
            else
            {
                TResult tresult = resultSelector(item, score);
                userScoreViewModel2List.Add(tresult);
            }
        }
    }
    return userScoreViewModel2List;
}

最后这个还想说一点,我们使用通用的selectMany方法,并没有使用反射,而是通过外面传递的lamdba表达式,也就是外面传递的方法来实现这个通用的功能,从这里也可以看出来通过方法传递来实现一个功能是非常灵活的,可以写出很通用的代码。

当然我们这里是解析的集合,而不是解析的sql语句,如果是解析sql语句我们这里应该就是传递的表达式树了,ef中解析sql语句的selectMany原理后面有机会在说



关于TNBLOG
TNBLOG,技术分享。技术交流:群号677373950
ICP备案 :渝ICP备18016597号-1
App store Android
精彩评论
{{item.replyName}}
{{item.content}}
{{item.time}}
{{subpj.replyName}}
@{{subpj.beReplyName}}{{subpj.content}}
{{subpj.time}}
猜你喜欢