C # Review notes (4)--c#3: Innovative Ways to write code (extension method)

Source: Internet
Author: User





extension Methods


The extension method has several requirements:





    • You want to add some members to a type;
    • You do not need to add any more data to an instance of the type;
    • You can't change the type itself, because it's someone else's code.


For static methods in C#1 and c#2, the extension method is a more elegant solution.


Grammar


Not all methods can be used as extension methods-it must have the following characteristics:


    • It must be in a non-nested, non-generic static class (so it must be a static method);
    • It must have at least one parameter;
    • The first parameter must append the This keyword as a prefix;
    • The first parameter cannot have any other modifiers (such as out or ref);
    • The type of the first parameter cannot be a pointer type.


Let's try to write an extension method for the Stream class:


public static class StreamUtil
    {
        const int bufferSize = 8192;
        public static void CopyToo (this Stream inputStream, Stream outPutStream)
        {
            var buffer = new byte [bufferSize];
            int read;
            while ((read = inputStream.Read (buffer, 0, buffer.Length))> 0)
            {
                outPutStream.Write (buffer, 0, read);
            }
        }

        public static byte [] ReadFull (this Stream input)
        {
            using (MemoryStream stream = new MemoryStream ())
            {
                CopyToo (input, stream);
                return stream.ToArray ();
            }
        }
    }
Extension methods must be declared in a top-level static class, not a nested static class.

Extension methods pretend to be instance methods of another class. Let's see how to use them:

 static void Main (string [] args)
        {
            WebRequest request = WebRequest.Create ("https://www.baidu.com");
            using (WebResponse response = request.GetResponse ())
            using (var resStream = response.GetResponseStream ())
                using (var outPut = File.Open (@ "C: \ Users \ jianxin \ Desktop \ test.txt", FileMode.Open))
            {
              resStream.CopyToo (outPut);
            }
            Console.WriteLine ("done!");
            Console.ReadKey ();
        }
The reason why CopyTo is changed to CopyToo is because Stream has now implemented this extension method. If it has the same name as the instance method, this extension method will not be called.

Some principles
Generally speaking, if you call a member such as a method behind an object, the compiler will first look for its instance members. If it does not find it, it will look for the appropriate extension method in the imported namespace.

To decide whether to use an extension method, the compiler must be able to distinguish the extension method from other methods in a static class that happen to have the appropriate signature. To do this, it checks whether the classes and methods have the System.Runtime.CompilerServices.ExtensionAttribute attribute. However, the compiler does not check which assembly the feature came from. This means that you can write this feature class yourself in C # 2 or earlier to satisfy this search strategy of the compiler. But who is still using C # 2 or 1? If multiple suitable versions are encountered, the "better choice" principle will still be used to select the most suitable one.

Calling the extension method on a null reference
Calling a method on a null reference results in a NullRefrenceException. However, extension methods can be called without causing an exception.

public static class NullUtil
    {
        public static bool IsNull (this object obj)
        {return obj == null;
        }
    }
 static void Main (string [] args)
        {
            object obj = null;
            Console.WriteLine (obj.IsNull ()); // true
            obj = new object ();
            Console.WriteLine (obj.IsNull ()); // false
            Console.ReadKey ();
        }
If IsNull is an instance method then a NullRefrenceException will be thrown, but the extension method will not. You can try it. This notation contrasts sharply with string.IsNullOrEmpty ().

Enumerable
Almost all the functions of LINQ are obtained by using Enumerable and Queryable extension methods.

Enumerable is not an extension method: Range

 var collection = Enumerable.Range (0, 10);
            foreach (int item in collection)
            {
                Console.WriteLine (item);
            }
This example is not mentioned because it is special, but because of one of its characteristics: delayed execution. The Range method doesn't really construct a list with the appropriate numbers, it just generates those numbers at the right time. In other words, constructing an enumerable instance does not do most of the work. It just prepares things so that the data is available in a "just-in-time" way in the right place. This is called delayed execution and is a core part of LINQ.

It is possible to return another enumerable instance based on an enumerable instance, which is very common in LINQ: collection.Reverse ();

Buffering and streaming technology
The extension methods provided by the framework will try to "stream" or "pipe" the data as much as possible. When an iterator is required to provide the next element, it usually obtains an element from the iterator to which it is linked, processes that element, and returns the result that meets the requirements without occupying more storage space. When performing simple conversion and filtering operations, this is very easy to do and the available data is very efficient to process. However, for some operations, such as reversing or sorting, all data is required to be available, so all data needs to be loaded into memory to perform batch processing. The difference between buffering and pipe transmission is similar to the difference between loading the entire DataSet to read data and using a DataReader to process one record at a time. When using LINQ, you must think about what you really need. A simple method call can severely affect performance.

Streaming is also called lazy evaluation, and bufferring is also called eager evaluation. For example, the Reverse method uses deferred execution, which does nothing until the first call to MoveNext. But then eagerly evaluated the data source.

Both lazy evaluation and enthusiastic evaluation are deferred evaluation methods, as opposed to immediate execution. A post on Stack Overflow illustrates the difference between them well (see http://stackoverflow.com/questions/2515796/deferred-execution-and-eager-evaluation).

Filter with where and chain method calls together
The where extension method is a simple but very powerful way to filter collections. Take a look at the code:

 static void Main (string [] args)
        {
            var collection = Enumerable.Range (0, 10)
                                       .Where (x => x% 2! = 0)
                                       .Reverse ();
            foreach (int item in collection)
            {
                Console.WriteLine (item);
            }
            Console.ReadKey ();
        }
The above code uses where to filter out all even numbers in the sequence, and then uses Reverse to reverse the sequence. Hope you have noticed a pattern at this point-we have chained the method calls together. string.Replace () is one such pattern. LINQ made special adjustments for data processing, linking individual operations together to form a pipeline, and then letting information flow through this pipeline.

There is an efficiency problem: If the above code calls Reverse first and then where, will it be as efficient as the previous call sequence? No, Reverse must calculate even numbers, and even numbers are eventually discarded. After filtering out this part of data with where first, the calculation to be performed by Reverse becomes significantly smaller.

Projection with select method and anonymous type
The most important projection method in Enumerable is select, which manipulates an IEnumerable <TSource> and converts it into an IEnumerable <TResult>. It uses the technique of delayed execution, and only truly performs the projection when each element is requested.

static void Main (string [] args)
        {
            var collection = Enumerable.Range (0, 10)
                .Where (x => x% 2! = 0)
                .Reverse ()
                .Select (x => new {Original = x, SquareRoot = Math.Sqrt (x)});
            foreach (var item in collection)
            {
                Console.WriteLine (item);
            }
            Console.ReadKey ();
        }
Sorting with OrderBy
In Linq, usually through OrderBy or OrderByDescending. You can also continue sorting. Use ThenBy and ThenByDescending. It should be noted that sorting does not change the original set, it returns a new sequence-LINQ operators are side-effect-free: they do not affect the input, nor do they change the environment. Unless you are iterating through a sequence of natural states (such as reading data from a network stream) or using delegate parameters with side effects. This is a functional programming approach that makes code more readable, testable, composable, predictable, robust, and thread-safe.

GroupBy
Suppose you want to observe the number of bugs in your program and group them:

bugs.GroupBy (bug => bug.AssignedTo)
                .Select (list => new {Developer = list.Key, Count = list.Count ()})
                .OrderByDescending (x =>x.Count);
 The result is an IGrouping <TKey, TElement>. GroupBy has multiple overloaded versions, the simplest one is used here. Then select the key (the name of the developer) and the number of bugs assigned to them. After that, we sorted the results to show the developers assigned the most bugs first.

When researching the Enumerable class, I often feel confused about what happened-for example, an overloaded version of GroupBy actually has 4 type parameters and 5 "normal" parameters (3 are delegates). However, don't panic-just follow the steps described in the previous chapter to slowly sort out and assign different types to different type parameters until you clearly see how the method looks. This makes it easier to understand. These examples are not specific to a method call, but I hope you can appreciate the tremendous role played by chaining method calls. In this chain, each method gets an original set and returns another original set in some form-some values may be filtered out in the middle, they may be sorted, every element may be transformed, some values may be aggregated , Or do other processing. In many cases, the final code is easy to read and understand. In other cases, it will at least be much simpler than equivalent code written in previous versions of C #.

Using ideas and principles
Because extension methods support such chained calls. Therefore, we have the concept of fluent interfaces, such as OrderBy ThenBy. Just like natural language.

 

C # Review Notes (4)-C # 3: Innovating the way to write code (extension methods)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.