c# 高级编程 12章247页 【LINQ】【扩展方法】

138 阅读2分钟

扩展方法

  • 扩展方法可以
    • 把方法添加到最初没有提供该方法的类
    • 把方法添加到某个接口中,例如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 returnitem
  • 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