. NET in-depth parsing of the LINQ framework (I: A prelude to LINQ elegance)

Source: Internet
Author: User

Read the catalogue:

    • 1.LINQ Overview
    • 2.LINQ Elegant Prelude to Notes
      • 2.1. Implicit type (the final type of object is inferred automatically by the editor based on an expression)
      • 2.2. Object initializer (simplifies the process of creating and initializing objects)
      • 2.3.Lambda Expressions (improvements to anonymous methods, added type inference for delegate signatures and good combination with expression trees)
      • 2.4. Extension method (allows independent behavior of the type to be added without modifying the type's internal code)
      • 2.5. Anonymous type (the type inferred by the object initializer, which is automatically created after compilation)
      • 2.6. Expression tree (using data structure to represent program logic code)
    • The main design model of 3.LINQ frame
      • 3.1. Chained design mode (System logic is designed in a pipelined way)
      • 3.2. Chained Query method (stepwise processing of each work point in a query expression)
    • Core design principle of 4.LINQ frame
      • 4.1. Languages above the Managed language (LINQ query expressions)
      • 4.2. The foundation of a managed language construct (the LINQ dependency Common interface corresponds to the method of the query operator docking)
      • 4.3. In-depth IEnumerable, ienumerable<t>, Enumerable (the entry to the LINQ to object framework)
      • 4.4. In-depth IQueryable, iqueryable<t>, queryable (the entry to the LINQ to provider framework)
      • 4.5.LINQ query interface for different data sources
    • 5. Dynamic LINQ Query (dynamically build expression<t> expression tree)
    • 6.DLR Dynamic Language runtime (Dynamic Language runtime on top of the CLR)
1 ". LINQ Overview

The LINQ abbreviation Language Integrated Query is designed to address the. NET platform for unified data query.

Microsoft was originally designed to solve the object/relational mapping solution by simply using T-SQL-like syntax for querying and manipulating data entities. But a good thing can eventually evolve into a benign evolution that is now. NET platform, a powerful unified data source query interface.

We can use LINQ to query objects in memory (LINQ to Object), Databases (LINQ to SQL), XML Documents (LINQ to XML), and more custom data sources.

Using LINQ to query a custom data source requires the IQueryable, Iqueryprovider two heavyweight interfaces provided to us by the LINQ framework. The following article will explain, here first to understand.

Before LINQ did occur, we needed to master a lot of interface technologies for different data source queries, and we needed repetitive and tedious iterative iterations for object collections. For databases we need to use database query languages such as many t-sql\pl-sql. For XML we need to use XMLDOM programming interface or XPath and so on, we need to grasp too much of things, that is laborious and easy to forget.

So how does LINQ do a uniform access to different data sources? Its elegance is not fixed in two days a day, but ultimately thanks to the designers of C #, who have made C # so perfectly evolved that it ultimately created the Elegance of LINQ.

Let's take a look at each evolution of C #, where it creates an elegant prelude to LINQ.

2 ". The notes of the Elegant Prelude of LINQ
    • 2.1. Implicit type (the final type of object is inferred automatically by the editor based on an expression)

The implicit type is actually the editor playing the syntax of sugar, but it is to a great extent convenient for us to encode. The familiar JS friend is not unfamiliar to the implicit type, but the implicit type in JS is very different from the C # implicit type here. Although syntactically the same is defined by the Var keyword, the end result is quite different.

JS is based on the principle of dynamic type system design, and C # is based on static type system design, the two are different in the design principle, to the last run more differently.

Here by the way recommended a C # aspect of a more in-depth book "In-depth analysis of C #", Want to learn more about C # friends can see. This book has two version, the second edition is our familiar Yao Qirin eldest brother translation is very good. Thanks Yao Ge Gaomi for translating such a good book for us. This book explains in great detail the history of C #, including many of the historical origins of design. From the master's handwriting, very has the study reference value, the rare good book.

We quickly end this section with a short, small example.

View Code

Here I define a List<order> object and initialize several values, and then iterate through the data subkeys through the foreach. In fact, this kind of writing is very normal, it is easy to understand. However, since C#3 has added the Var keyword and the editor has supported the automatic analysis type for the VAR keyword, see the code below.

View Code

The editor can intelligently analyze what kind of definition we define, in other words we really need the editor to help us determine the object type at compile time. This is common in LINQ, and when you write a LINQ query expression, it is unrealistic to judge the type of object to return, but it is idealized to parse from the compiler from the syntax rules. Because LINQ relies on extension methods for chained queries, types are not deterministic at the time of writing. The following article will be explained in detail, here first to understand.

    • 2.2. Object initializer (simplifies the process of creating and initializing objects)

In fact, the object initializer is a simple syntax improvement, in order to facilitate the construction of our object. (the so-called plan is only owed to the east wind, this is the LINQ solution.) So you have to have everything you need before you can. )

So how much use is the object initializer? Let's go ahead and see what the syntax is.

View Code

Note: Object initializers can only be used on properties, public fields.

The properties are initialized with the effects written in this syntax and are used directly (order. Count=10;order. Orderid= "123"; order. Ordername= "Purchase order";) are equal.

It is also easy to understand that the collection initializes the multiline syntax using curly braces. The data assignment for the class-specific sub-object is the same.

I think the code has the pursuit of friends will love this grammar, indeed very beautiful.

    • 2.3.Lambda Expressions (improvements to anonymous methods, added type inference for delegate signatures and good combination with expression trees)

I don't think any friends are unfamiliar with lambda expressions, and if you're unfamiliar with lambda expressions, it's okay to look after them. You can go back to cram school later.

In LINQ query expressions, the elegance that lambda makes is everywhere. A strongly typed chained query is achieved by encapsulating an anonymous method.

Lambda is an attribute in a functional programming language that simply represents a function. Not only is it convenient to use, it is also convenient to find definitions. It is simple to define when needed, and avoids the tedious need to define a method before using the delegate. There is a syntactic difference between a lambda expression and an anonymous delegate, both of which are encapsulated in an anonymous function. But their presence was anonymously delegated earlier than Lambda. So it seems that lambda looks elegant.

Let's take a look at a small example, a simple look at how Lambda works, and most importantly, where is it better than anonymous delegation?

View Code

We define a generic method for filtering data, which is a generic method that requires the type argument to be specified when used. The method has two parameters, the first is the data collection to be filtered, and the second is a logical rule encapsulation to be filtered.

Let's look at the code of the call:

View Code

The logical rule we define here is that as long as it is greater than 3 I will extract it and return it. Obviously here (int item) = {return item > 3;} The syntax section is the lambda expression, which conveniently encapsulates the logic of the method. From this point of view, Lambda is significantly more powerful than anonymous delegates, and most importantly, it also supports the generic type inference feature.

So what is the generic type inference?

In fact, the type inference of generics is that the simple point is that the type arguments do not need to be specified by us, and the editor can automatically derive types of type arguments by parsing the potential relationships in the expression.

It's a bit hollow, we still see the specific code is clearer.

View Code

I've modified the code above to not need to display the specified generic type argument calls, which is also possible.

When we define the filter<t> generic method, T in the func<t,bool> generic delegate is defined as the parameter type of the anonymous function, so we need to specify the type argument when we use it (int item) Item in the to represent the type parameter parameters that the delegate will use. In the editor's view, the generic placeholder T that we used to define the generic method filter also happens to be the type of the call parameter used in the filter method's form parameter data type func<t,bool>. So the syntax analysis rules here can accurately infer the same generic type argument that we use. ( it is to be remembered that the current IDE editor supports only generic type inference for method invocations, meaning that other aspects of generic use do not support implicit type inference, or that we need to manually add type arguments.) )

Here, by the way, about lazy loading techniques, lazy loading techniques are useful in collection class traversal, especially in LINQ. Most of the time we do not deal with the collection in real time, that is to say, I get the collection of data is not a one-time, need to I need a specific item when I have to deal with the code about the acquisition. I changed the filter code a little bit:

View Code

Using the yield keyword here, we can form an automatic state machine structure within the method. The simple point is that the system will help us automatically implement an object that inherits the Ienumerable<t> interface, before we need to implement the iterator interface members ourselves, which is time consuming and difficult to perform. With the method defined in this way, we will only be called when we iterate over the specific collection, which is a great performance boost.

The shortcomings of generic type inference;

Of course, there is still a shortage of type inference, and here's an article from our old brother Zhao: "The C # compiler's strange Problem of type inference for generic method calls"; I have had a headache in my actual work, and I would like to share it with you. According to common sense, I define a generic delegate in the parameters of a generic method, whose formal parameter types are the same placeholders, but if I use a method with formal parameters as the argument of the delegate, it is not possible to type inference, and then use the parameterless method as the delegate parameter is completely free of problems. You must then use a lambda expression to do the correct type inference, which I do not understand if it is possible to pass a method with parameters directly as a parameter of a delegate without actually type inference. Post the code to discuss the issue with you.

I define two methods, these two methods have no meaning, just one with parameters, one without parameters.

Method with no parameters:

View Code

There are parameter methods:

View Code

The order object is just a type, and there is no special meaning here.

Two methods with a Func delegate to demonstrate the type inference of generics:

View Code

The problem here is that if I use the Getorderlist method as a parameter to the getmodellist<tresult> (func<tresult> getfunc) generic method, there is no problem. The editor can actually infer the type of the generic type. But if I use Getorderlistbymodel as Getmodellist<tsource, tresult> (Func<tsource, tresult> GetFunc) An overloaded version of a generic method cannot actually infer the type. In fact, the TResult in the Func is already the return type of the method, TSource is also the parameter type of the method, according to the truth is completely can be inferred type. But I tried a lot of ways just can't afford it. Oddly enough, if I use a lambda expression with a parameter and return type as Getmodellist<tsource, tresult> (Func<tsource, tresult> getfunc) The correct type inference for the method's parameters.

Legend of Method invocation:

In the second line of code in the diagram, the Getmodellist method is called with a method that has parameters, and the actual type inference is not possible.

Summary: According to this analysis, it seems that the generic type inference for a method is limited to lambda expressions? If it is not why the parameter is more than the argument can not be type inference? Let's keep this question for the answer.

    • 2.4. Extension method (allows independent behavior of the type to be added without modifying the type's internal code)

The intent of an extension method is to add behavior to an object without modifying the object's internal code. This convenience greatly improves our program's extensibility, although this little extensibility is not trivial in the code, but it will play a big role if the use of clever words. The extension method is very important for LINQ support, and many objects are originally built with. On the NET2.0 Framework, LINQ is the. NET3.0 technique, and it is challenging to add behavior to objects without affecting the original object.

Then we can embed it seamlessly inside the previous object using the extension method. Such requirements are common in framing design, and most typically we write one. NET2.0 versions of DLL files are used as client programs, then we need to control the DLL objects in the. NET2.0 version on the server side. Like the traditional WinForm framework, we can use an ORM entity as the control data source for a form, allowing the ORM entity to form a natural mapping between the controls on the form, including the ability to assign values and set values. However, such entities are serialized to the service layer, then inspected into the BLL layer and then into the DAL layer, this time the ORM framework needs to use the entity for the corresponding database operations. So how do we use it. Does the NET3.0 feature add other behaviors to the ORM? If there is no extension method here is a rogue. With the extension method we can build the extension method with the. Net3.0dll, add a friend reference to the. Net2.0dll, and then extend the ORM entity.

Let's look at a small example to see if the extension method is used;

View Code

This is just for demonstration, it's relatively simple. I have defined an order class and a ordercollection class, and it seems that Ordercollection has no method at this time, so we add an extension method to add a write calculation method to the Ordercollection class, such as summarizing, summing, and so on.

How do I define an extension method?

The extension method must be a static method in a static class, and we define an extension method count for the Ordercollection class.

View Code

The first parameter of the extension method must be the this key at the beginning, followed by the object type to be extended, and then an instance object reference that extends the object at run time. If there is no reference to the instance object I think the extension method is also unconscious. So here we use the Count method to summarize the total number of order objects. By ordercollectionobject The object reference we can get the instantiated Ordercollection object.

View Code

It is also important to note that if we define the extension method in another namespace, we must use in the current CS code to apply the extension method in the namespace, otherwise the editor will not look for the object you are currently using the extension method, avoid. It is also important to note that we need to carefully consider the access rights of the object members when we design the objects that may be used by the extension method later on, if we are to design the objects that will be used by the extension method to be protected or private, it may involve the inability to control the maximum force.

    • 2.5. Anonymous type (the type inferred by the object initializer, which is automatically created after compilation)

Anonymous types are also better understood, as the name implies that anonymous types are types that do not have type definitions. This type is automatically generated by the editor and is used only in the current context. Talk less, let's look at an example;

View Code

Defining an anonymous type is similar to a normal definition type, except that it is a pair of curly braces after new, followed by the property name and value you need to use.

The scope of the anonymous type;

Anonymous types have a congenital disadvantage in their use, and they cannot pass anonymous types between methods because of the lack of a type definition to display. To get the property values of an anonymous type, you can only get the runtime Property object dynamically by reflection, and then get the value of the property through the Property object. An anonymous type is created when it is used, so it has a complete object definition metadata at run time, so it is perfectly understandable to get the data through reflection.

Below we use the type defined above to get its various properties.

View Code

Legend:

By reflecting the way we can get to the anonymous type of the property member, and then through the property information in a smooth get to the value of the property.

    • 2.6. Expression tree (using data structure to represent logical code)

The expression tree is a priority in LINQ, and elegance is actually here. We have from anonymous delegates to Lambda lambda expressions in the directory tree to the present, we see. The language on the net platform is becoming more powerful. We have no reason not to accept its beauty. So what is the expression tree and what does it exist to solve, or what needs to be?

We've covered the concept of lambda notation above, which is an elegant way to write anonymous functions. Inside the lambda expression is the code for the program logic, which is compiled by the compiler to form the run-time path of the program and cannot be manipulated as a data structure in the program. For example, in the lambda expression I wrote this piece of code: (Student Stu) =>stu.name== "King Qingyue pui", then this code after the compiler compiled into a familiar Microsoft intermediate language IL. So in many cases we need to show the performance of its operations as data results, we need to parse it artificially and turn it into another language or call mode. So why do you need such a superfluous in your program, and you can't express the equivalent expression of a lambda expression in a string-like way? The purpose of this is to guarantee a strongly typed operation that does not cause errors to be checked at compile time. And if we use string to express the structure of the logic, then we can only know its correctness at runtime, so the correctness is very fragile, do not know in what circumstances will be problematic. So if we have a strong type of runtime check, we can safely use an expression like lambda, and then parse it into a variety of logical equations when needed.

In the System.Linq.Expression namespace of the. NET3.5 Framework, a group of child objects representing the expression tree represented by an expression abstract class is introduced. The purpose of this set of objects is to fully represent the data meaning of the logical expression at runtime, so that we can easily get and parse the structure. In order for ordinary lambda expressions to be parsed into expression object set data structures, it is necessary to use the expression<t> generic type, which derives from LambdaExpression, which represents an expression of the lambda type. By using an object of the delegate delegate type as a type parameter in the expression<t>, the editor automatically converts the lambda expression into an expression tree data structure. We seem to have an example;

View Code

The editor takes a different approach to each of these two lines of code, looking at tracking object status.

If you do not use expression<t> as the wrapper for the delegate type, the type will be the normal delegate type.

If expression<t> is used as a wrapper for a delegate type, the compiler parses it into an object that inherits from the System.Linq.Expression.LambdaExpression type. Once you become an object, everything is much better, and we can get to the data structure within expression in a very simple way.

An object model of an expression tree;

The above simply introduces the meaning and basic principle of the expression tree, so what is the inheritance of the expression tree or what is its object model like? We only have to sort out its overall structure so that we can use and expand it later.

Let's examine its internal structure.

(Student Stu) =>stu. name== "King Qingyue", I define a lambda expression that we can view as a whole. What is an expression of the whole, which means that it can be represented by an expression object, this is our LambdaExpression object. The essence of an expression tree is to use objects to express the logical structure of the code, so for a complete lambda expression we must be able to completely disassemble it to be able to parse it, then you can split the lambda expression into two parts and then continue splitting the last two parts, This recursive disassembly will naturally form an expression tree, which is actually the tree structure inside the data structure. So in C # we can easily construct a tree structure, and the tree is full of polymorphism.

(Student Stu) =>stu. Name= "King Qingyue Culture", is a kind of tree-shaped structure it? Let's take a look at its run-time tree structure, and then look at how it's constructed by unfolding an abstract inheritance graph.

The first object in the expression<t> is a generic object, and by tracing information you can see that the,expression<t> object inherits from the LambdaExpression object. The LambdaExpression object inherits from the expression abstract class, and the ToString method is overridden in the abstract, so we see the string representation after ToString.

The lambda expression object consists of two main parts, the argument and the logical subject from left to right, and corresponds to the parameters and body two public properties. The parameters is a self-read list of all parameters, using a system.collection.objectmodel.readonlycollection<t> generic object to store.

Here perhaps you have the parameter question, seemingly the expression tree construction is really perfect, each detail has the specified object to represent. Yes, in the. NET3.5 framework, many objects are introduced to represent the logical nodes of an expression tree. These objects are directly or indirectly inherited from the expression abstract class, which represents an abstract expression node. We all know that expression nodes are various and need to be materialized before they can be used directly. So there are only two properties in the base class expression, one is public expressiontype NodeType {get;}, which represents the type of the current expression node, and another public type type {get;}, Represents the static type of the current expression. What is a static type, that is, when it is not the type of the expression tree, the specific point is the delegate type. Because after a delegate type is expression<t> generic wrapper, the compiler compiles it automatically into the data structure type of the expression tree, so you need to save the actual type of the current node for future use.

Summary: In fact, some of the preparations for LINQ have been completed, from a series of syntax enhancements. The addition of the NET5.0 class library has paved the way for the arrival of the subsequent LINQ. The following few summaries will be the most exciting moments, please do not miss Oh.

. NET in-depth parsing of the LINQ framework (I: A prelude to LINQ elegance)

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.