C# in Depth (第十章 扩展方法)

10.1 未引入扩展方法之前的状态

  • 想为一个类型添加一些成员
  • 你不需要为类型的实例添加任何更多的数据
  • 你不能改变类型本身,因为是别人的代码
  • 想处理的是接口不是类

为流提供简单功能的一个简单工具类

  public static class StreamUtilNoExtensions
    {
        const int BufferSize = 8192;

        public static void Copy(Stream input,
                                Stream output)
        {
            byte[] buffer = new byte[BufferSize];
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, read);
            }
        }

        public static byte[] ReadFully(Stream input)
        {
            using (MemoryStream tempStream = new MemoryStream())
            {
                Copy(input, tempStream);
                if (tempStream.Length == tempStream.GetBuffer().Length)
                {
                    return tempStream.GetBuffer();
                }
                return tempStream.ToArray();
            }
        }
    }

 

用StreamUtil将Web响应流复制到一个文件

  class UsingStreamUtilWithoutExtensions
    {
        static void Main()
        {
            WebRequest request = WebRequest.Create("http://manning.com");
            using (WebResponse response = request.GetResponse())
            using (Stream responseStream = response.GetResponseStream())
            using (FileStream output = File.Create("response.dat"))
            {
                StreamUtilNoExtensions.Copy(responseStream, output);
            }
        }
    }

10.2 扩展方法的语法
10.2.1声明扩展方法

  • 它必须在一个非嵌套的,非泛型的静态类中(所以必须是一个静态方法)
  • 它至少要有一个参数
  • 第一个参数必须附加this关键字作为前缀
  • 第一个参数不能有其他任何修饰(比如out, ref)
  • 第一个参数的类型不能是指针类型。

包含扩展方法的StreamUtil类

 public static class StreamUtilWithExtensions
    {
        const int BufferSize = 8192;

        public static void CopyTo(this Stream input,
                                  Stream output)
        {
            byte[] buffer = new byte[BufferSize];
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, read);
            }
        }

        public static byte[] ReadFully(this Stream input)
        {
            using (MemoryStream tempStream = new MemoryStream())
            {
                CopyTo(input, tempStream);
                if (tempStream.Length == tempStream.GetBuffer().Length)
                {
                    return tempStream.GetBuffer();
                }
                return tempStream.ToArray();
            }
        }
    }

10.2.2调用扩展方法

 用扩展方法复制一个流

class UsingStreamUtilWithExtensions
    {
        static void Main()
        {
            WebRequest request = WebRequest.Create("http://manning.com");
            using (WebResponse response = request.GetResponse())
            using (Stream responseStream = response.GetResponseStream())
            using (FileStream output = File.Create("response.dat"))
            {
                responseStream.CopyTo(output);
            }
        }
    }

10.2.3扩展方法是怎样被发现的

编译器根据using找到扩展方法。

10.2.4在空引用上调用方法

 static class NullUtil
    {
        public static bool IsNull(this object x)
        {
            return x == null;
        }
    }

10.3.1 从Enumerable开始起步

 用Enumerable.Range打印数字0~9

    class RangeEnumeration
    {
        static void Main()
        {
            var collection = Enumerable.Range(0, 10);

            foreach (var element in collection)
            {
                Console.WriteLine(element);
            }
        }
    }
  •  延迟执行 Range方法并不会真的构造含有适当数字的列表,它只是在恰当的时间生成那些数。换言之,构造的可枚举的实例并不会做大部分工作。它只是将东西准备好,使数据能够在适当的位置以一种 just-in-time的方式提供。这称为延迟执行,是LINQ的一个核心部分。

 

 用Reverse方法来反转一个集合

 class RangeReversal
    {
        static void Main()
        {
            var collection = Enumerable.Range(0, 10)
                           .Reverse();

            foreach (var element in collection)
            {
                Console.WriteLine(element);
            }
        }
    }
  • 效率问题:缓冲和流式技术。
  • 框架提供的扩展方法会尽量尝试对数据进行“流式”(stream)或者"管道"(pipe)传输。要求一个迭代器提供下一个元素时,它通常会从它链接的迭代器获取一个元素,处理那个元素。再返回符合要求的结果,而不用占用自己更多的存储空间。执行简单的转换和过滤操作时,这样做非常简单,可用的数据处理起来也非常高效。
  • 但是,对于某些操作来说比如反转或排序,就要求所有数据都处于可用状态,所以需要加载所有数据到内存来执行处理
  • 缓冲和管道传输方式,这两者的差别很像是加载整个DataSet读取数据和用一个DataSet读取数据和用一个DataReader来每次处理一条记录的差别
  • 流式传输(streaming)也叫惰性求值(lazy evaluation),缓冲传输(bufferring)也叫热情求值(eager evaluation)。

10.3.2 用Where过滤并将方法调用链接到一起

用Lambda表达式作为where方法的参数,从而只保留奇数

 class RangeFiltering
    {
        static void Main()
        {
            var collection = Enumerable.Range(0, 10)
                           .Where(x => x % 2 != 0)
                           .Reverse();

            foreach (var element in collection)
            {
                Console.WriteLine(element);
            }
        }
    }
  • “返回相同的引用”模式用于易变类型,而“返回新实例(该实例为原始实例更改后的副本)“模式则用于不易变类型。

 10.3.3 插曲:似曾相识的where方法

 10.3.4 用Select方法和匿名类型进行投影

  class RangeProjection
    {
        static void Main()
        {
            var collection = Enumerable.Range(0, 10)
                                       .Where(x => x % 2 != 0)
                                       .Reverse()
                                       .Select(x => new { Original = x, SquareRoot = Math.Sqrt(x) });

            foreach (var element in collection)
            {
                Console.WriteLine("sqrt({0})={1}",
                                  element.Original,
                                  element.SquareRoot);
            }
        }
    }

 10.3.5 用OrderBy方法进行排序

 class RangeOrdering
    {
        static void Main()
        {
            var collection = Enumerable.Range(-5, 11)
                                       .Select(x => new { Original = x, Square = x * x })
                                       .OrderBy(x => x.Square)
                                       .ThenBy(x => x.Original);

            foreach (var element in collection)
            {
                Console.WriteLine(element);
            }
        }
    }

 

posted @ 2015-09-17 17:35  莱茵哈特  阅读(369)  评论(0编辑  收藏  举报