扩展方法的具体定义我就不写自行去搜索,我简单说一下扩展方法,定义一个扩展方法就在一个静态类中,定义一个静态方法,方法的第一个参数就是你需要扩展类型,之后你可以可以在这个类型的实例直接调用这个方法:
比如我们为string
增加一个截断的如果超过多长就截取后面加…的扩展方法。
public static class ExtendMethod
{
public static string ToLength(this string text, int length = 15)
{
if (string.IsNullOrWhiteSpace(text))
{
return "空";
}
else if (text.Length > length)
{
return ($"{text.Substring(0, length)}...");
}
else
{
return text;
}
}
}
public static int ToInt(this int? i)
{
return i ?? 0;
}
int? k = 23;
int l = 4;
//int r = k??0 + l;
int r = k.ToInt() + l;和上行等同
public static string ToStringCustom<T>(this T t)
{
if (t is Guid)
{
return t.ToString().Replace("-", "");
}
//.....
else
{
return t.ToString();
}
}
我们首先设想一个场景,比如我们有一个student
数据集合,我们常规需要对student进行不懂的数据过滤做不通的安排,常规情况下的数据过滤,我们需要年龄小于30:
List<Student> studentList = this.GetStudentList();
//年纪小于30
var list = new List<Student>();
foreach (var item in studentList)
{
if (item.Age < 30)
{
list.Add(item);
}
}
或者我们需要Name长度大于2的
//Name长度大于2的
var list = new List<Student>();
foreach (var item in studentList)
{
if (item.Name.Length > 2)
{
list.Add(item);
}
}
或者我们需要更多的条件:
//N个条件叠加
var list = new List<Student>();
foreach (var item in studentList)
{
if (item.Id > 1
&& item.Name != null
&& item.ClassId == 1
&& item.Age > 20)
{
list.Add(item);
}
}
这样写是不是很麻烦,而且很不友好,不能有一个办法,我们只需要传过滤条件要实现,这样委托就派上用场了,我们通过委托来作为过滤条件,使用Func
返回条件bool值来判断数据过滤,我们通过添加一个扩展方法来实现这个数据的过滤:
public static class ExtendMethod
{
//泛型完成对不同类型的数据过滤
public static List<T> ElevenWhere<T>(this List<T> resource, Func<T, bool> func)
{
var list = new List<T>();
foreach (var item in resource)
{
if (func.Invoke(item))//通过委托带入数据过滤条件
{
list.Add(item);
}
}
return list;
}
}
这样我们就可以这样来过滤数据了:
var result = studentList.ElevenWhere<Student>(s => s.Age < 30);
var result = studentList.ElevenWhere(s => s.Name.Length > 2);
var result = studentList.ElevenWhere(item => item.Id > 1
&& item.Name != null
&& item.ClassId == 1
&& item.Age > 20);
等同上面数据过滤效果。这里面的委托使用的是lambda表达式,如果不清楚lambda表达式,参考上一章C#Lambda表达式
这不就是我们平时见到的linq的表达式,其实linq表达式实现的原理就是和上面一样
我们自定义的linq表达式返回的List
而实际的linq都是IEnumerable
,我们崽升级一下代码
public static IEnumerable<T> ElevenWhereIterator<T>(this IEnumerable<T> resource, Func<T, bool> func)
{
var list = new List<T>();
foreach (var item in resource)
{
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
再升级一下代码:
//迭代器模式,完成了数据的按需获取,延迟加载
public static IEnumerable<T> ElevenWhereIterator<T>(this IEnumerable<T> resource, Func<T, bool> func)
{
foreach (var item in resource)
{
Console.WriteLine("进入数据检测");
Thread.Sleep(100);
if (func.Invoke(item))
{
yield return item;//yield 跟IEnumerable配对使用
}
}
}
我们使用了迭代器yield(这个以后详细讲一章)为什么使用可枚举类型IEnumerable
,这个官方定义的可枚举类型,IEnumerable的介绍可以参考先说IEnumerable,我们每天用的foreach你真的懂它吗
到这里我们应该基本上明白linq是什么东西了。
//这里有一堆学生 每个学生都转换成别的对象
Console.WriteLine("**********投影**********");
//查询运算符方式
var list = studentList.Where<Student>(s => s.Age < 30)
.Select(s => new
{
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高级班" : "其他班"
});
//表达式方式
var list = from s in studentList
where s.Age < 30
select new
{
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高级班" : "其他班"
};
上面两种写法是等同效果。
//IN查询
var list = studentList.Where<Student>(s => s.Age < 30)
.Where(s => new int[] {
1, 2, 3, 4 }.Contains(s.ClassId))
.Select(s => new
{
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高级班" : "其他班"
});
var list = studentList.Where<Student>(s => s.Age < 30)//条件过滤
.Select(s => new//投影
{
Id = s.Id,
ClassId = s.ClassId,
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高级班" : "其他班"
})
.OrderBy(s => s.Id)//排序
//.ThenBy//2个都生效
.OrderByDescending(s => s.ClassId)//倒排 最后一个生效
.Skip(2)//跳过几条
.Take(3)//获取几条
;
var list = from s in studentList
where s.Age < 30
group s by s.ClassId into sg
select sg;
foreach (var data in list)
{
Console.WriteLine(data.Key);
foreach (var item in data)
{
Console.WriteLine($"{item.Id} {item.Name} {item.Age}");
}
}
//group by
Console.WriteLine("********************");
var list = from s in studentList
where s.Age < 30
group s by s.ClassId into sg
select new
{
key = sg.Key,
maxAge = sg.Max(t => t.Age)
};
var list = studentList.GroupBy(s => s.ClassId).Select(sg => new
{
key = sg.Key,
maxAge = sg.Max(t => t.Age)
});
var list = from s in studentList
join c in classList on s.ClassId equals c.Id//不能用==只能equals
select new
{
Name = s.Name,
CalssName = c.ClassName
};
var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
{
Name = s.Name,
CalssName = c.ClassName
});
var list = from s in studentList
join c in classList on s.ClassId equals c.Id
into scList
from sc in scList.DefaultIfEmpty()//
select new
{
Name = s.Name,
CalssName = sc == null ? "无班级" : sc.ClassName//c变sc,为空则用
};
var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
{
Name = s.Name,
CalssName = c.ClassName
}).DefaultIfEmpty();//为空就没有了
IQueryable<Student> list = studentList.AsQueryable();
list.Where<Student>(s => s.Age > 30);//操作数据库
这是一种伟大的封装思想,希望通过一种模式完成一切数据源的访问,让开发者彻底变成小白,(当年,潮流是傻瓜式,这种很厉害;现代化的开发有更高要求)