C# Tutorial: Features About Anonymous Types

This article is reproduced from: https://www.cnblogs.com/skykang/archive/2011/03/03/1970194.html Author: skykang Please indicate the statement when reprinting.

First let's look at an example, suppose we don't have a Person class, and the only properties we care about are Name and Age. The following code demonstrates how we can construct an object without declaring a type:

   1: var tom = new { Name = Tom, Age = 4 };
   2: var holly = new { Name = Holly, Age = 31 };
   3: var jon = new { Name = Jon, Age = 31 };
   4: Console.WriteLine({0} is {1} years old, jon.Name, jon.Age);

As you can see, initializing an anonymous class is very similar to the object initializers we mentioned earlier—the only difference is that the type name between new and the opening curly brace is gone. We are using implicit local variables, because this is our only option - we don't have any type name with which to declare the variable. As you can see from the last line of code above, the type is used for the Name and Age properties, they can be read and their value is implicitly The value assigned in the type initializer, therefore, the output will be Jon is 31 years old. The property will have the same type as the expression inside the initializer - Name is of type string, Age is of type int. and Like object initializers, methods and constructors can also be used in implicit object initializers to do whatever you want.

Now you know why implicitly typed arrays are so important. Suppose we want to create an array containing the entire family members, and then iterate to find the total age. The following code will do the job - and demonstrate some other interesting things about Traits of anonymous types.

   1: var family = new[]
   2: {    3:     new { Name = Holly, Age = 31 },   4:     new { Name = Jon, Age = 31 },   5:     new { Name = Tom, Age = 4 },   6:     new { Name = Robin, Age = 1 },   7:     new { Name = William, Age = 1 }   8: };   9: int totalAge = 0;  10: foreach (var person in family)  11: {   12:     totalAge += person.Age;  13: }  14: Console.WriteLine(Total age: {0}, totalAge);











From what we've learned about implicitly typed arrays, we can deduce one important thing: all family members are of the same type. Because if they all create a new type using anonymous type initializers, then obviously Array types cannot be declared correctly. In a given Assembly, if two anonymous types have the same number of properties, and they have the same name and type, and the same order of appearance, the compiler will treat them as Treated as the same type. In other words, if we swapped the Name and Age properties of one of them, the compiler would generate a new anonymous type—similarly, if we introduced an additional property on a new line, Or use the long type instead of Age's int type, all of which will cause the compiler to generate a new anonymous type.

Implementation details: If you've ever looked at the IL code of an anonymous class, you should know that even if two anonymous object initializers have the same property names and appearances, but use different types, the compiler will generate two different types, Instead, they are actually generated from a single generic class. The generic class is parameterized, but its enclosing constructed type will differ depending on the type parameters given to different initializers.

We can use a foreach expression to apply to the above array, just like we do for a collection. The type is inferred by the compiler, and the type of person is also the type of the array. Again, we can use the same variable with in different instances because they all have the same type.

The above code also verifies that the Age property is a strongly typed int, otherwise we will get an error if we try to calculate the sum of age. The compiler understands anonymous types, and VS2008 provides more information through tooltips. Now we know enough information about anonymous types, let's see what the compiler actually does for us.

member of anonymous type

Anonymous types are created by the compiler and included in the compiled Assembly in the same way that anonymous methods and iterator blocks are created. The CRL treats them as ordinary types, when in fact they are ordinary types— If you change it from an anonymous type to a normal type, and code all the behavior by hand, we won't see any change. An anonymous type contains the following members:

A constructor responsible for all initializers, whose parameters will be in the same order and type as in the anonymous object initializer, with the same names.
Public read-only properties
Private read-only fields, used to support property
overloading With Equals, GetHashCode and ToString
that's all, no excuses are implemented, no cloning and serialization capabilities - just a constructor, some properties and a few normal methods from object.

Constructors and property completion are both obvious things. The equality of two implementations from the same anonymous type is determined by comparing the Equals methods of all property types in turn. The hash code generation is also a similar, by calling the Equals method of each property GetHashCode and then combine the results. The method of combining multiple hash codes into a composite is not specified, so you can't rely on it in your code - the only thing you can be confident about is that two equal object instances must return The same hash value, and two unequal instances may return unequal hash values. Of course, all of this is only valid if the property type implements GetHashCode and Equals also follow the normal rules.

Note that because the property is read-only, the anonymous class is immutable, so the type of the property is also immutable. Because of this immutability, you don't have to worry about the property being changed after assignment, and there is no problem across threads.

Projection initializers

In the anonymous object initializers we've seen so far we've been using simple name/value pairs -- Name=Jon, Age=31, although sometimes we do, but more often in real programming, we might Will copy the property value from an existing object. Sometimes we have some way to read the value, but more often a copy is enough.

Without LINQ, it's a bit difficult to give a convincing example, but let's go back to our Person class, assuming we have a good reason why we want to convert a collection of Person instances to another similar collection whose elements are Contains a name and a flag indicating whether the person is an adult. Given an appropriate variable, we can use the following code:

   1: new { Name = person.Name, IsAdult = (person.Age >= 18) }

The code will certainly work, and it's certainly not unwieldy to use this method if you just set a single name property - but if you're copying a lot of properties, you should consider something else. C# 3 provides a shortcut: if You don't specify a property name, but just use the expression to evaluate the value, then the compiler will use the last part of the expression as the property name. This is called a divergent initializer. This means that we can use the above code Rewrite as:

   1: new { person.Name, IsAdult = (person.Age >= 18) }

It is very common to turn an anonymous object initializer into a diverging initializer - usually when you want to copy some properties from one object to another, often as part of a join operation. The following shows the complete Code, using List.ConvertAll method and anonymous proxy:

   1: List<Person> family = new List<Person>
   2: {    3:     new Person {Name=Holly, Age=31},   4:     new Person {Name=Jon, Age=31},   5:     new Person {Name=Tom, Age=4},   6:     new Person {Name=Robin, Age=1},   7:     new Person {Name=William, Age=1}   8: };   9: var converted = family.ConvertAll(delegate(Person person)  10: {   11:     return new { person.Name, IsAdult = (person.Age >= 18) }; }  12: );  13: foreach (var person in converted)  14: {   15:     Console.WriteLine({0} is an adult? {1},  16:     person.Name, person.IsAdult);  17: }














The above code initially uses a divergent initializer, and we also show the value of anonymous methods and proxy type inference, without which we cannot keep the converted type still strongly typed. Because we cannot specify the TOutput parameter in the converter The type in it. After the conversion, we can use an iteration to iterate through the entire List and access the Name and IsAdult properties, but we're already using another type.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324126027&siteId=291194637