Linq的妙用之筛选数据到字典
在本文,笔者将教大家如何将数据筛选并保存到字典中,解决字典 key 不唯一导致的异常。
背景:
OnClick 同学奇思妙想,说要给方法加属性来标记他们,并通过标记好的索引的排列组合以及增减来决定方法执行顺序以及哪些方法需要被执行到,按他的说法呢,说是要用到建造者模式中。
代码:
先弄一个车的模板:
public class Car
{
public string Name;
public string Color;
public int id;
[MethodStep(step = 1)]
public void SetName()
{
}
[MethodStep(step = 2)]
[MethodStep(step = 3)]
[MethodStep(step = 4)]
private static void SetColor()
{
}
[MethodStep(step = 3)]
public void SetID()
{
}
}
Tips:
- 仅仅是示意哈,所以方法内的实现省略。
- MethodStep 就是整个思路的重点,他决定了哪个方法需要执行,以及怎么个顺序执行。
紧接着我们使用扩展方法采集这些 Attribute 的信息,这也是本文的重点:Linq 的妙用
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
namespace IFramework
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class MethodStepAttribute : Attribute
{
public int step;
public MethodStepAttribute() { }
public MethodStepAttribute(int step) { this.step = step; }
}
public static class IMethodRunnerExtension
{
public static T RunStepMethods<T>(this T t, int[] steps) where T : class
{
var methodsDic = typeof(T)
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
.Where(v =>v.IsDefined(typeof(MethodStepAttribute)) &&v.GetParameters().Length == 0)
.SelectMany(v => v.GetCustomAttributes(typeof(MethodStepAttribute)), (m, s) => new {(s as MethodStepAttribute).step, m })
.GroupBy(v=>v.step,v=>v.m)
.ToDictionary(v=>v.Key,v=>v.First());
steps.ForEach((step) =>
{
if (methodsDic.ContainsKey(step))
methodsDic[step].Invoke(t, null);
else
Log.E("No Such Func Step " + step);
});
return t;
}
}
}
然后我们使用 Onclick 同学的思路构建一部车:
Car car = new Car().RunStepMethods(new int[] { 1, 2, 3 });
//当然,你也可以偷工减料,少做一道工序:
Car car = new Car().RunStepMethods(new int[] { 1, 3 });
对这个思路有兴趣的可以去他的码云仓库瞅瞅
讲解:
静下心来再配合需求与思路,上面的 Linq 扩展方法还是很好理解的。
-
Where
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
- API解读:签名内的 this表明这是一个扩展方法,Where后面的一对尖括号“<>”表明他是泛型,需要传的只有一个参数,该参数是有一个返回值的委托。
- 作用:就是断言并采集那些符合要求的所有的数据
- 在本例中,就是初步筛选出符合条件的 MethodInfo(可以看到在本例,这个条件是方法必须加了特性且没有参数)。
-
SelectMany
public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector);
- API 解读:选择集合性质的字段, 扩展方法 ,2 个委托类型的参数,前面参数为集合选择,它负责将你给的所有对象的指定字段采集起来,这个字段必须是数组这样的数据结构。(不然 selectmany 里面的Many还有啥意义呢?)。后面的参数是委托 Func 的返回值,通过使用匿名类型采集一些你关心数据。
- 作用 : 就是选择集合性质的字段,并生成新的映射关系。
- 在本例中,先将每个方法中的一个或者多个 MethodStepAttribute 采集并将 MethodStepAttribute 实例中的 step 字段与 MethodInfo 使用一个个匿名类型的实例搜集起来。
-
GroupBy
public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector);
- API解读:使用指定的键值作为依据对原数据进行分组 ,第二个参数则决定了要从原数据取出哪一个字段作为这个分组的值(值为集合)的元素。
-
在本例中,通过 step 对 匿名类型的实例进行分组,然后取出 MethodInfo 作为最终输出。示例代码可以预见,Step = 3 时 MethodInfo 存在2个哦
step是 分组信息的 key ,MethodInfo 是值
-
ToDictionary
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector);
- API解读:通过指定的 key 将原数据或者原数据中指定的其他数据(字段)封装到字典。
- 在本例中,笔者使用分组信息的 key 作为字典的 key(也就是 step ) ,将分组信息的值的第一位 MethodInfo 作为值 存储到字典。
- 所以,该方法的第二个参数 就是避免字典添加元素时 key 重叠的关键一步,也是 GroupBy 的意义所在哈!
-
本例中为了简洁,使用了大量的 lambda 表达式。
如所见:v=>v==1
是 等效于(v)=>{return v==1;}
的。所以回过头去再看看,还会有不好理解的嘛?
结语:
通过这个示例我们 Get 到了 Linq 的更多魅力,是不是好满足哦!
还没有理解完全的,建议上手试试哦~