扩展方法
- 扩展方法可以:
- 把方法添加到,最初没有提供该方法的类中
- 把方法添加到,某个接口中,例如
IEnumerable<T>
- 这样一来,实现了这个接口的类,都能使用这个扩展方法
- 扩展方法不能:
- 访问它扩展的类型的私有成员
- 是一种调用静态方法的新语法
- 不必新定义一个静态类,可以把这个静态方法包含在,合适的已有类里
string s = "Hello";
//string类型,被扩展为包含静态方法Foo()
s.Foo();
//上面的用法,等同于,下面的用法
StringExtension.Foo(s);
//扩展方法:
public static class StringExtension
{
public static void Foo(this string s)
{
Console.WriteLine($"Foo invoked for {s}");
}
}
输出:
Foo invoked for Hello
IEnumerable<T>
的扩展方法 Where<T>()
的实现
- 入参是
Func<T, bool> predicate
- 对
IEnumerable<T>
的每一个item(T
类型),调用这个predicate - 用于判断item是否应该被
yield return
- 因为使用了yield return, 所以编译器创建了一个枚举器
- 对
- 返回值是
IEnumerable<T>
- 一堆被
yield return
的item
- 一堆被
foreach
返回值,访问枚举中的项之后,查询才会运行,并返回结果
public static IEnumerable<T> Where<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
{
foreach(T item in collection)
{
if(predicate(item))
{
yield return item;
}
}
}
IEnumerable<T>
的扩展方法 的使用
定义查询表达式时,查询不会执行。查询会在迭代数据项时执行。
static void ExtensionMethods()
{
var champions = new List<Racer>(Formula1.GetChampions());
//只是定义了查询,查询不会执行
IEnumerable<Racer> brazilChampions =
champions.Where(racer => racer.Country == "Brazil")
.OrderByDescending(racer => racer.Wins)
.Select(racer => racer);
//查询会在迭代数据项时执行
foreach (Racer r in brazilChampions)
{
Console.WriteLine($"{r:A}");
}
}
数据源变化后的查询结果
- 在迭代中执行查询时,会调用
IEnumerable<T>
的扩展方法Where<T>
等 - 如果数据源发生变化,再次迭代以执行查询时,也是在新数据源的上做查询,即能检查到数据源的变化
但是特别注意:
IEnumerable<T>
的扩展方法ToArray()
,ToList()
比较特殊,调用了这俩扩展方法之后,如果数据源发生改变,再次迭代以执行查询时,就不是在新数据源上做查询了,即检查不到数据源的变化
static void DeferredQuery()
{
var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" };
var namesWithJ = from n in names
where n.StartsWith("J")
orderby n
select n;
Console.WriteLine("First iteration");
//在迭代中执行查询(调用IEnumerable<T>的扩展方法Where<T>等)
foreach (string name in namesWithJ)
{
Console.WriteLine(name);
}
Console.WriteLine();
//打印 Juan
//改变数据源
names.Add("John");
names.Add("Jim");
names.Add("Jack");
names.Add("Denny");
Console.WriteLine("Second iteration");
//在迭代中执行查询(调用IEnumerable<T>的扩展方法Where<T>等)
//使用变化后的数据源,即能检测数据源的变化
foreach (string name in namesWithJ)
{
Console.WriteLine(name);
}
Console.WriteLine();
//打印 Juan John Jim Jack
}
输出:
First iteration
Juan
Second iteration
Jack
Jim
John
Juan
ToList
static void DeferredQuery2()
{
var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" };
//这个查询里,有IEnumerable<T>的扩展方法ToList()
var namesWithJ = (from n in names
where n.StartsWith("J")
orderby n
select n).ToList();
Console.WriteLine("First iteration");
foreach (string name in namesWithJ)
{
Console.WriteLine(name);
}
Console.WriteLine();
//数据源变化
names.Add("John");
names.Add("Jim");
names.Add("Jack");
names.Add("Denny");
//不能检测数据源的变化
Console.WriteLine("Second iteration");
foreach (string name in namesWithJ)
{
Console.WriteLine(name);
}
Console.WriteLine();
}
输出:
First iteration
Juan
Second iteration
Juan