C # 3.0 LINQ preparation work

Source: Internet
Author: User
Tags sin

Local variables

Implicit typing allows you to modify the type with var. Using Var modifier is just a compiler for us to encode, the type itself is still strongly typed, so when the compiler cannot infer the type (for example, if you initialize a variable without assigning it a value, or give null, you cannot infer its type at this time), an error will occur with the Var modifier. In addition, implicit types can only be used on local variables.

There are several opportunities for using implicit types:

    1. When the type of the variable is too long or difficult to speculate, but the type itself is not important, such as your LINQ statement using GroupBy, then generally few people can accurately infer the type of the result ...
    2. When a variable is initialized, the variable type can be learned at this time based on the type behind new, so it does not affect readability
    3. The object you iterate over in the Foreach loop, you don't typically need to explicitly indicate the type

In general, if you use an implicit type to cause your code to be less readable, you should instead change the explicit type. The general second rule is already an unwritten rule. ReSharper If you do not use implicit typing when you detect variable initialization, you will also be reminded that you can use VAR instead.

implicit types in LINQ: You can use Var to decorate the type returned by a LINQ statement. Generally speaking, the return type of a LINQ statement is usually a long name and is not very obvious. Without an implicit type, it can be painful to write code.

Auto-implemented Properties

It's time to use auto-implemented properties all over the land. Note the use of auto-implemented properties in structs (note that fields are not required) requires an explicit call to the parameterless constructor of this (). This is a difference between a struct and a class.

public struct Foo    {public        int a {get; private set;}        Foo (int A): this ()        {            a = A;        }    }

If the above code removes this () error, these properties cannot be used until the default parameterless constructor sets the property of the struct to its default value. If you change the properties of the above code to a field, there is no problem even if you do not call this ().

Anonymous types (Anonymous type)

Anonymous types allow you to create a type directly in parentheses. Although you do not need to specify a specific type of member, the members of the anonymous type are strongly typed.

        static void Main (string[] args)        {            var tom = new {Name = "Tom", age =-};            Console.WriteLine ("{0}: {1}", Tom. Name, Tom. age);        }

After initializing an anonymous type, you can use the dot notation to get the members of an anonymous type, just like the actual type, but the variable tom can only be decorated with Var or object. If two anonymous types have the same number of members, and all members have the same type name and value type, and appear in the same order, the compiler considers them to be the same type.

        static void Main (string[] args)        {            var family = new[]            {                new {name = "Tom", age = $},                new {name = "Jerr Y ", age = +}            ;            var cat = new {age = +, Name = "Cat"};            var dog = new {age = 2222222222222222, Name = "Dog"};        }

If the order of the attributes is swapped in the initialization, or if a property uses a long instead of an int, a new anonymous type is introduced.

An anonymous type contains a default constructor that gets all the initial values that you have assigned. In addition, it contains the type members you define, as well as several methods that inherit from the type of object (overridden equals, overridden GetHashCode, ToString, and so on). Two instances of the same anonymous type when judging equality, the way in which each member's value is compared in turn is used.

In LINQ, we can use anonymous types to load the data returned by a query, especially when you finally return several columns using a method such as Select or SelectMany. It is too tedious to customize a class for the return data in every query, although sometimes it is needed (ViewModel), but there are times when it is just for a one-time display of data. If the type you are creating is only used in one method, and there are only simple fields or properties and there is no method, you might consider using an anonymous type.

Expressions and Expressions trees (expression & expression tree)

Express is the meaning of expression (it has many other meanings, such as fast), and the noun suffix-sion is an expression.

Expressions are the most important component in today's programming languages. Simply put, an expression is a combination of variables, values, operators, and functions that represent a certain meaning. For example, the following are (C #) Expressions:

    • 3//constant-expression
    • A//variable or parameter expression
    • !a//unary logic non-expression
    • A + b//Two-dollar addition expression
    • Math.sin (a)//method invocation (lambda) expression
    • New StringBuilder ()//new expression

An important feature of an expression is that it can be infinitely combined, as long as it conforms to the correct type and semantics. The expression tree converts an expression into a tree structure, where each node is an expression. Expression trees are often used to convert to other forms of code. For example, LINQ to SQL interprets an expression tree as SQL.

Some of the most basic expressions
    • Constant expression: Expression.constant (the value of the constant);
    • Variable expression: Expression.parameter (typeof (Variable type), "Variable name")
    • A binary expression, which is an expression that requires two expressions to be manipulated as arguments: expressions. [A method of a two-tuple expression, such as subtraction, modulo, etc.] (expression 1, expression 2);
    • Lambda expression: A method that can accept a code snippet or a method call expression as a method, as well as a set of method parameters. Lambda is a Greek letter that cannot be translated. There are many Greek letters, such as alpha, beta and so on. The reason why this letter is chosen is because of mathematical reasons (there is a lambda operation mathematically)
Build one of the simplest expression trees 1+2+3

An expression tree is a tree of objects, where each node is an expression. It can be said that each expression is an expression tree, in particular, some expressions can be considered as only one node of the expression tree, such as a constant expression. The expression class under the System.Linq.Expressions namespace and its many subclasses are the implementations of this data structure. The expression class is an abstract class that mainly contains some static factory methods. An expression class also contains two properties:

    • Type: A. NET type that represents the evaluation of an expression, such as expression.constant (1) and Expression.add (Expression.constant (1), Expression.constant (2)) Types are Int32.
    • NodeType: Represents the kind of expression. For example the kind of expression.constant (1) is the type of Constant,expression.add (Expression.constant (1), Expression.constant (2)) is ADD.

each expression can be expressed as expressions an instance of a subclass. For example, binaryexpression represents an expression of various two-tuple operators, such as subtraction. It requires two operands (note that the operand is also an expression):

    public static binaryexpression Add (expression left, expression right);

The constructors for each subclass of expression are not exposed, and you can only use static methods provided by the expression class to create a tree of expressions.

To create an expression tree, we first draw the tree and find out what type of expression it needs. For example, if we were to create an expression tree for the expression 1 + 2 + 3, because it is too simple and does not contain more than one operation (if there is a multiplier to consider the priority), we can see that it requires only two expressions, a constant expression (described as three-way) and a two-tuple expression (describing addition), So it can be written like this:

ConstantExpression EXP1 = expression.constant (1); ConstantExpression EXP2 = expression.constant (2); Binaryexpression EXP12 = Expression.add (EXP1, EXP2); ConstantExpression EXP3 = expression.constant (3); Binaryexpression exp123 = Expression.add (EXP12, EXP3);

This should be very well understood. But what if we want to write Math.sin (a) expression tree of this expression? To solve this problem, the lambda expression comes up, and it can represent a method.

Using a lambda expression to represent a function

Our goal is to use a lambda expression to represent Math.sin (a) as an expression. A lambda expression represents a function, and now it has an input a (we use the variable expression parameterexpression to represent, it should be a double type), and a method call, This requires an expression of type Methodcallexpression, the method named Sin, in the math class. We need to use reflection to find out this method.

The code is as follows:

ParameterExpression ExpA = Expression.parameter (typeof (Double), "a"); Parameter Amethodcallexpression Expcall = Expression.call (typeof (Math). GetMethod ("Sin", BindingFlags.Static |   BindingFlags.Public), ExpA); Math.sin (a) lambdaexpression exp = Expression.lambda (Expcall, ExpA); A = Math.sin (a)

Using lambda expressions: by expression<tdelegate>

The expression<tdelegate> generic class inherits the LambdaExpression type, and its constructor accepts a lambda expression. Here tdelegate refers to a generic delegate, which can be either a func or an action. A generic class determines the type of the return type and the parameter statically.

For the last example, our input and output are a double type, so we need the delegate type is func<double, Double>:

Expression<func<double, double>> exp2 = d = Math.sin (d);

You can use the Compile method to compile expression<tdelegate> into a tdelegate type (in this case, the object type after compilation is func<double,double>), which is a an easy way to compile an expression tree as a delegate (you don't need to step back and use reflection). The compiler automatically implements the conversion.

You can then call directly to get the result of an expression calculation:

Expression<func<double, double>> exp2 = d = Math.sin (d); Func<double, double> func = Exp2.compile (); Console.WriteLine (func (0.5));
Exercise: Construct an expression tree using two methods (A, B, m, n) = + M * A * a + n * b * b

Assume that all variable types are double.

Code method:

(A, B, m, n) = = m * A * a +  n * b * bparameterexpression ExpA = Expression.parameter (typeof (Double), "a");//Parameter AP Arameterexpression EXPB = Expression.parameter (typeof (Double), "B"); Parameter Bparameterexpression expm = Expression.parameter (typeof (Double), "M"); Parameter Mparameterexpression ExpN = Expression.parameter (typeof (Double), "n"); Parameter Nbinaryexpression multiply1 = expression.multiply (expm, ExpA); Binaryexpression multiply2 = expression.multiply (multiply1, ExpA); Binaryexpression multiply3 = expression.multiply (ExpN, EXPB); Binaryexpression multiply4 = expression.multiply (Multiply3, EXPB); Binaryexpression add = Expression.add (Multiply2, multiply4);

Commission method:

Expression<func<double, double, double, double, double>> exp4 = (A, b, M, n) + m*a*a + n*b*b;var RET = exp 4.Compile (); Console.WriteLine (ret. Invoke (1, 2, 3, 4)); =3*1*1+4*2*2=3+16=19

With the expression<tdelegate> and compile methods, we can easily calculate the result of an expression. But if you step by step, we also need to manually traverse this tree. Why do you do this if you are so troublesome to use code to construct an expression? Just because you can insert other actions when you manually traverse and evaluate the result of an expression. LINQ to SQL is a recursive traversal of the expression tree, translating LINQ statements into SQL queries, which cannot be overridden by delegates.

Not all lambda expressions can be converted to an expression tree. You cannot convert a lambda with a block of code into an expression tree. An assignment cannot be done in an expression because it is not represented in an expression tree.

Reference: Expression tree finger South http://www.cnblogs.com/Ninputer/archive/2009/08/28/expression_tree1.html

Extension methods (Extension method)

An extension method can be understood as an extension (addition) of some functionality to an existing type (an existing type can be a custom type and a type in a. Net class library) that is attached to the type.

When we want to extend the functionality of a class, there are several ways to directly modify the code of the class, which can result in backward-compatible corruption (not conforming to the open and closed principle). The first is derived subclasses, but this increases the amount of maintenance and cannot be done for structures and sealed classes at all. The extension method allows us to still modify the type without creating a subclass, without changing the type itself .

The extension method must be defined in a static type, and all extension methods must be static. Or that sentence, when you understand the type object, you naturally understand why the extension method must be static. (It should be in the object's method table when it is created from a type object)

The first input parameter of the extension method is prefixed with this (the type of the first parameter represents the type being extended). The extension method must have at least one input parameter.

All subclasses of the extended type are automatically given the extension method.

Consider using extension methods when you have specific logic within your project and are based on a more general class. If you want to add some members to a type, but you cannot change the type itself (because it does not belong to you), consider using an extension method. For example, if you need to frequently determine whether a string is an email, you can extend the string class and place it in a single type called Stringextension for easy administration. You can then use this method conveniently by calling String.isemail.

C # Two particularly striking classes are available: Enumerable and Queryable . Both are in the System.Linq namespace. In these two classes, there are many extension methods. most of the extensions of Enumerable are ienumerable<t>, queryable Most of the extensions are iqueryable<t> . They give a powerful set of query capabilities, which together form the Important foundation of LINQ.

What is a closure (Closure)? How does C # implement a closure?

Closures are a language feature that refers to a function that acquires variables outside its scope and interacts with it. The word closure obviously comes from the verb close, a bit of the noun meaning of the verb.

With an anonymous function or lambda expression, we can implement a simple closure:

static void Main (string[] args)        {            //external variable            var i = 0;            A lambda expression captures an external variable            //declares a method within the scope of an external variable            methodinvoker m = () =            {                //using an external variable                i = i + 1;            };            M.invoke ();            Print out 1            Console.WriteLine (i);        }

Here the function interacts with the variable i from the outside.

Anonymous functions (Anonymous function)

The anonymous function appears in C # 2.0, which allows its operation to be specified inline in the creation of a delegate instance.

For example we can write:

            Compare (c1, C2, delegate (Circle A, circle B)            {                if (A.radius > B.radius) return 1;                if (A.radius < B.radius) return-1;                return 0;            });

Syntax for anonymous methods: First a delegate keyword, then a parameter (if any), followed by a block of code that defines the operation of the delegate instance. Contravariance does not apply to anonymous methods, you must specify a parameter type that exactly matches the delegate type (in this case, two circle types).

The return value is obtained by adding return to the anonymous method. In. NET 2, there are very few delegates that have a return value (because the previous return value is overwritten after the delegate chain is formed by multiple delegates), but most of the delegates in LINQ have return values (through the Func generic delegate).

The main benefit of using anonymous methods is that you do not need to name a function, especially one that is used only once, or a very short, simple function. When you get to the lambda expression, you'll find that in LINQ there are lambda expressions everywhere, and in fact they are anonymous functions (that is, delegates). If we were to build a function externally every time we were using LINQ frequently, the volume of the code would be greatly increased.

Another important point of the anonymous function is that it automatically forms a closure. Variables defined within an anonymous function are called local variables of anonymous functions, and unlike normal functions, anonymous functions can use capture variables in addition to local variables, passed in variables. When an external variable is used by an anonymous function in a function method, it is called the variable is captured (that is, it becomes a capture variable).

The capture is an instance of a variable instead of a value, that is, the capture variable inside the anonymous function is the same as the external variable. When a variable is captured, the variable of the value type is automatically "upgraded" to become a sealed class. creating a delegate instance does not cause execution.

The role of the capture variable (captured Variable)

capturing variables makes it easy for us to get the variables we need when we create an anonymous method (or delegate). For example, if you have a list of integers and want to write an anonymous method to filter out another list that is less than a certain limit, if you do not capture a variable, in the anonymous method we can only hardcode the value of limit, or use the original delegate to pass the variable to the target method of the delegate.

        Static ienumerable<int> Filter (list<int> alist, int limit)        {            //LAMBDA expression captures external variable limit            return Alist.where (A = a < limit);        }
Capturing the lifetime of a variable

As long as there is a delegate referencing this capture variable, it will always exist. Whether the capture variable is a value type or a reference type, the compiler generates an extra class for it.

public delegate void MethodInvoker ();        static void Main (string[] args)        {            MethodInvoker m = createdelegate ();            Because a delegate reference a,a will persist            //Capture variable A is no longer on the stack, the compiler treats it as an extra Class            //createdelegate method has a reference to an instance of this extra class            //When the delegate is recycled, This extra Class M () will not be recycled            ;        }        Static MethodInvoker createdelegate ()        {            int a = 1;            MethodInvoker m = () =            {                Console.WriteLine (a);                a++;            };            M ();            return m;        }

Print out 1 and 2. Output 1 is because the variable a is available when createdelegate is called. When CreateDelegate returns, the call to M,a is still available and does not disappear. A closure is formed by being caught, and a is changed from a value type on a stack to a reference type. The compiler generates an additional sealed class (the name is less readable, such as C__DISPLAYCLASS1), it has a member A and a method, and the code inside the method is the code in the MethodInvoker.

CreateDelegate holds a reference to a type C__DISPLAYCLASS1, so it has always been able to use member A in C__DISPLAYCLASS1.

 Internal class Program {public delegate void MethodInvoker ();            [Compilergenerated] Private sealed class <>c__displayclass1 {public int A;                public void <createdelegate>b__0 () {Console.WriteLine (THIS.A);            this.a++; }} private static void Main (string[] args) {Program.methodinvoker MethodInvoker = Progra            M.createdelegate ();            MethodInvoker ();        Console.readkey (); } private static Program.methodinvoker createdelegate () {Program.<>c__displayclass1 <&G            T;c__displayclass = new Program.<>c__displayclass1 ();            <>C__DISPLAYCLASS.A = 1; Program.methodinvoker MethodInvoker = new Program.methodinvoker (<>c__displayclass.<createdelegate>b__0            );            MethodInvoker ();        return methodinvoker; }    }
Interview questions: Shared and unshared capture variables

When used with closures and for loops, if more than one delegate snaps to the same variable, there are two cases: snapping to only one instance of the same variable, and snapping to the same variable, but each delegate has its own instance.

        static void Main ()        {            int copy;            list<action> actions = new list<action> ();            for (int counter = 0; counter < counter++)            {                //Only one variable copy, which was created before the loop starts//                All delegates share this variable                copy = Counter;                Actions are not performed when a delegate is created                . ADD (() = Console.WriteLine (copy));            }            foreach (Action action in actions)            {                //executes the delegate when the copy current value                is printed//copy The current value is 9                Action ();            }            Console.readkey ();        }

In this example, the capture variable is copy, and it has only one instance (its definition is outside, it is captured, it is automatically promoted to the reference type), and all the delegates share the instance. Finally print out 10 out of 9.

        static void Main ()        {            int copy;            list<action> actions = new list<action> ();            for (int counter = 0; counter < counter++)            {                               copy = counter;                There are now 10 internal variables, each of which has an instance, and the values of the instances owned by different delegates are different                //Thus the delegate can output 0-9                int copy1 = copy;                Actions are not performed when a delegate is created                . ADD (() = Console.WriteLine (copy1));            foreach (Action action in actions)            {                //executes the delegate when printing the value of the Copy1                Action ();            }            Console.readkey ();        }

Use an internal variable to solve a problem where multiple delegates share a capture variable instance. The following code, which contains the two scenarios described above, allows you to think about the final print result:

        static void Main (string[] args)        {            var list = new list<methodinvoker> ();            for (int index = 0; index < 5; index++)            {                var counter = index*10;                List. Add (delegate                {                    Console.WriteLine ("{0}, {1}", counter, index);                    counter++;                    });            }            List[0] ();            LIST[1] ();            LIST[2] ();            LIST[3] ();            LIST[4] ();            List[0] ();            List[0] ();            List[0] ();            Console.readkey ();        }

Five MethodInvoker are built within the loop. They share an instance of the variable index, but each has its own instance of the variable counter. So in the final printed result, the value of index will always be 5, and the value of counter is different each time.

Finally, the first delegate is executed three times, at this time the value of counter will use the first, the first delegate runs after the counter value, it will be played 1, after printing 2,3 the same. If you execute the second delegate one time, you will be 11. This fully demonstrates that each delegate holds an instance of counter, and that they are independent of each other. The value of index is 5 regardless of how many times an arbitrary delegate is executed.

Capturing variable changes in a foreach loop

In C # 5, the behavior of the Foreach loop changes and no more delegates share the behavior of a variable. So even if we don't declare an internal variable, the method will print out the results that are easy to understand:

        static void Main ()        {            list<string> values = new List<string> {"A", "B", "C"};            var actions = new list<action> ();            foreach (string s in values)            {                //anonymous method capture variable s                //analogy for loop the last 10 9,s the last value is C                //In theory will print out three C                //But in C # 5, Will print out the A,b,c                actions. ADD (() = Console.WriteLine (s));            foreach (Action action in actions)            {                action ();            }            Console.readkey ();        }

For the For statement, however, the behavior is the same as before, and you still need to be aware of the problem of the capture variable being shared.

C # 3.0 LINQ preparation work

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.