.NET中扩展方法和Enumerable

2019 年 7 月 7 日 DotNet

(给DotNet加星标,提升.Net技能


转自:lingfeng95
cnblogs.com/zhao123/p/11089911.html

LINQ是我最喜欢的功能之一,程序中到处是data.Where(x=x>5).Select(x)等等的代码,她使代码看起来更好,更容易编写,使用起来也超级方便,foreach使循环更加容易,而不用for int..,linq用起来那么爽,那么linq内部是如何实现的?我们如何自定义linq?


我们这里说的linq不是from score in scores  where score > 80 select score;而是System.Linq哦。


了解Ling之前先要了解扩展方法,因为linq的实质还是扩展方法。


扩展方法


扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 


扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。 


例如:


namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this string str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}

} }


//添加引用
using ExtensionMethods;
//使用
string s = "Hello Extension Methods";
int i = s.WordCount();


微软扩展方法建议


微软MSDN上的建议:通常,建议只在不得已的情况下才实现扩展方法,并谨慎地实现。只要有可能,都应该通过创建从现有类型派生的新类型来达到这一目的。


扩展方法建议


1. 当功能与扩展类型最相关时,可以考虑使用扩展方法。


2. 当对第三方库进行扩充的时候,可以考虑使用扩展方法。


3. 当您不希望将某些依赖项与扩展类型混合使用时,可以使用扩展方法来实现关注点分离。


4. 如果不确定到底使用还是不使用扩展方法,那就不要用。


扩展方法是C#语言的一个很好的补充,她使我们能够编写更好,更容易读的代码,但是也应该小心使用,不恰当的使用扩展方法可能导致可读性降低,使测试困难,容易出错。


System.Linq


System.Linq用起来那么好,她内部是如何实现的,当然是查看源码了。


Where源码


public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);
if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate);
if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);
return new WhereEnumerableIterator<TSource>(source, predicate);
}


这个方法就是一个扩展方法,对数据进行了处理,具体的处理都是在对象中的MoveNext中


public override bool MoveNext() {
if (state == 1) {
while (index < source.Length) {
TSource item = source[index];
index++;
if (predicate(item)) {
current = item;
return true;
}
}
Dispose();
}
return false;
}


可以看出就是一个循环处理,如果你觉得还是不清楚,可以看WhereIterator方法


static IEnumerable<TSource> WhereIterator<TSource>(IEnumerable<TSource> source, Func<TSource, int, bool> predicate) {
int index = -1;
foreach (TSource element in source) {
checked { index++; }
if (predicate(element, index)) yield return element;
}
}

这下明白了,linq就是扩展方法,对数据进行处理,返回所需要的数据,知道了原理之后,可以写自己的linq扩展方法了。


我想写一个带有控制台输出的Where扩展方法


public static IEnumerable<TSource> WhereWithLog<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null)
{
throw new ArgumentNullException("source", "不能为空");
}
if (predicate == null)
{
throw new ArgumentNullException("predicate", "不能为空");
}
int index = 0;
foreach (var item in source)
{
Console.WriteLine($"Where item:{item},结果:{predicate(item)}");
if (predicate(item))
{
yield return item;
}
index++;
}
}


实现一个打乱数据的扩展方法,这里的方法用了约束,只能是值类型。


public static IEnumerable<T> ShuffleForStruct<T>(this IEnumerable<T> source) where T : struct
{
if (source == null)
throw new ArgumentNullException("source", "不能为空");
var data = source.ToList();
int length = data.Count() - 1;
for (int i = length; i > 0; i--)
{
int j = rd.Next(i + 1);
var temp = data[j];
data[j] = data[i];
data[i] = temp;
}
return data;
}


到此为止是不是觉得Enumerable中的方法也就是那么回事,没有那么难,我也可以实现。


应评论的需要增加whereif,条件为true执行左边的条件,false执行右边的条件,例如:data.WhereIf(x => x > 5, x => x + 10, x => x - 10)


public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, Func<TSource,bool> predicate, Func<TSource, TSource> truePredicate, Func<TSource, TSource> falsePredicate)
{
if (source == null)
{
throw new ArgumentNullException("source", "不能为空");
}
if (predicate == null)
{
throw new ArgumentNullException("predicate", "不能为空");
}
if (truePredicate == null)
{
throw new ArgumentNullException("truePredicate", "不能为空");
}
if (falsePredicate == null)
{
throw new ArgumentNullException("falsePredicate", "不能为空");
}
foreach (var item in source)
{
if (predicate(item))
{
yield return truePredicate(item);
}
else {
yield return falsePredicate(item);
}
}
}


或者简单的whereif,true执行条件,false不执行,例如:data.WhereIf(x => x > 5,true)


public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, bool condition)
{
return condition ? source.Where(predicate) : source;
}

推荐阅读

(点击标题可跳转阅读)

 C#规范整理集合和Linq 

 .NET 架构开发应知应会

 C#使用InstallerProjects打包桌面应用程序


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

好文章,我在看❤️

登录查看更多
0

相关内容

.NET 框架(.NET Framework) 是由微软开发,一个致力于敏捷软件开发、快速应用开发、平台无关性和网络透明化的软件开发平台。
【斯坦福CS520】向量空间中嵌入的知识图谱推理,48页ppt
专知会员服务
100+阅读 · 2020年6月11日
【论文扩展】欧洲语言网格:概述
专知会员服务
6+阅读 · 2020年3月31日
【新书】Java企业微服务,Enterprise Java Microservices,272页pdf
《DeepGCNs: Making GCNs Go as Deep as CNNs》
专知会员服务
30+阅读 · 2019年10月17日
Github项目推荐 | Pytorch TVM 扩展
AI研习社
11+阅读 · 2019年5月5日
Python3.7中一种懒加载的方式
Python程序员
3+阅读 · 2018年4月27日
浅谈浏览器 http 的缓存机制
前端大全
6+阅读 · 2018年1月21日
Python NLP入门教程
Python开发者
9+阅读 · 2017年11月19日
Arxiv
101+阅读 · 2020年3月4日
Knowledge Distillation from Internal Representations
Arxiv
4+阅读 · 2019年10月8日
Arxiv
19+阅读 · 2018年10月25日
Arxiv
7+阅读 · 2018年3月19日
VIP会员
相关资讯
Github项目推荐 | Pytorch TVM 扩展
AI研习社
11+阅读 · 2019年5月5日
Python3.7中一种懒加载的方式
Python程序员
3+阅读 · 2018年4月27日
浅谈浏览器 http 的缓存机制
前端大全
6+阅读 · 2018年1月21日
Python NLP入门教程
Python开发者
9+阅读 · 2017年11月19日
相关论文
Arxiv
101+阅读 · 2020年3月4日
Knowledge Distillation from Internal Representations
Arxiv
4+阅读 · 2019年10月8日
Arxiv
19+阅读 · 2018年10月25日
Arxiv
7+阅读 · 2018年3月19日
Top
微信扫码咨询专知VIP会员