[Reading notes] C# study notes five: C#3.0 automatic attributes, anonymous attributes and extension methods


[Reading notes] C# study notes five: C#3.0 automatic attributes, anonymous attributes and extension methods

Preface

This chapter can be regarded as the biggest gain from reading this book. Lambda expressions make people use it after trial and error. C#3.0 can be said to subvert our coding style. Because Lambda requires a lot of space, so let’s summarize C#3.0 smart compilation. The many benefits brought to us by the browser, the next time we will introduce Lambda expressions separately. This article mainly includes:  automatic properties, implicit types, object collection initialization, anonymous types, extension methods.

Let's take a look at the changes brought by C#3.0.


1. The attributes that are automatically implemented are
before C#3.0. When defining attributes, you generally write code like the following:

Copy code

 1 class Person 2 {3 //Define private fields 4 private string name; 5 6 //Define readable and writable attributes 7 public string Name 8 {9 get{return name;}10 set{name = value;}11 }12 13 private int age;14 //Define read-only attributes 15 public int Age16 {17 get{return age;}18 }19}

Copy code

 

PS: There is a shortcut key here: private string name; select name and then Ctrl + R + E to quickly generate the Name attribute.

After C#3.0, for properties that do not require additional verification (properties that require additional verification must still be defined in the previous way), we can use the feature of automatic implementation to simplify the definition of the property, and no additional definition is required at this time A private field. The code is as follows:

Copy code

1 class Person2 {3 //Use automatically implemented attributes to define attributes 4 //Define read and write attributes 5 public string Name{get; set;} 6 //Define read-only attributes 7 public int Age{get; private set; }8 }

Copy code


PS: There is also a shortcut key here : Type prop and click the Tab key twice to generate the above properties, but you need to manually change the value.
Similar shortcut keys are: Enter cw and then click the Tab key twice to directly generate the Console .WriteLine();. There are many similar, so I won't list them here.

The reason why attributes can be defined in this way is mainly because the compiler will create a private field for us at compile time. With the decompiler tool, we can know that when using automatically implemented attributes, C# will help us create the necessary fields.
In addition, in the structure When using automatic attributes, it should be noted that all constructors need to explicitly call the parameterless constructor this, otherwise a compilation error will occur. Because only in this way, the compiler can know that all fields have been assigned.
However, the class There is no need to explicitly call the parameterless constructor, this is mainly determined by the design of the C# compiler, we just remember it.

Copy code

 1 public struct TestPerson 2 {3 public string Name {get; set;} 4 public int Age {get; private set;} 5 6 //In the structure, when the parameterless constructor this() is not explicitly called, yes Compile-time error 7 public TestPerson(string name) 8: this() 9 {10 this.Name = name;11 }12}

Copy code

 

2,隐式类型 var关键字
C#是强类型语言, 在定义一个变量时, 需要声明变量的类型. 然而类型的长度过长,就可能影响代码的可读写性. 为了避免这样的问题, C#3.0 引入了隐式类型,即可以使用关键字var来声明变量或数组.
var关键字告诉编译器去根据变量的值来推断其类型.

Copy code

1 class Program2 {3     static void Main(stirng[] args)4     {5         //用var声明局部变量6         var stringValue = "Barry Wang";7         stringValue = 2;8     }9 }

Copy code

 

这里就会报错"无法将类型int 隐式转换为string". 调试会发现stringValue是string类型的.
使用隐式类型有一些限制, 包括以下几点:
(1)被声明的变量是一个局部变量, 不能为字段
(2)变量在声明时必须被初始化, 因为编译器要根据变量的赋值来推断类型
(3)变量不能初始化为一个方法组, 也不能为一个匿名函数

 

3,对象集合初始化
在C#3.0之前定义类, 我们往往需要定义多个构造函数来完成不同情况下的初始化, C#3.0 提供了对象初始化器, 它减少了我们在类中定义的构造函数代码, 从而使代码更加简洁.

Copy code

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5         //没有对象初始化器时的代码 6         Person p = new Person("BarryWang", 25); 7         p.Weight = 60; 8         p.Height = 170; 9     }10 }11 12 public class Person13 {14     public string Name { get; set; }15     public int Age { get; set; }16     public int Weight { get; set; }17     public int Height { get; set; }18 19     //定义不同情况下的初始化函数20     public Person() { }21     public Person(string name) { } 
22     public Person(string name, int age) { }23     public Person(string name, int age, int weight) { }24     public Person(string name, int age, int weight, int height) 
25     {26         this.Name = name;27         this.Age = age;28         this.Weight = weight;29         this.Height = height;30     }31 }

Copy code

 

在C#3.0引入对象初始化之后, 我们就不需要定义多个构造函数了, 前面的代码可以简化为如下的形式:

Copy code

 1 class Program 2 { 3     static void Main(string[] args) 4     { 5         //使用对象初始化器初始化 6         Person p = new Person() {Name = "Barry Wang", Age = 25, Weght = 60, Height = 70}; 7         //下面这行代码和上面一行是等价的, 只不过下面省略了构造函数的圆括号而已 8         Person p2 = new Person {Name = "Barry Wang", Age = 25, Weght = 60, Height = 70}; 9     }10 }11 12 public class Person13 {14     public string Name { get; set; }15     public int Age { get; set; }16     public int Weight { get; set; }17     public int Height { get; set; }18 }

Copy code

 

利用反编译工具可以知道编译器为对象做了如下处理: 首先C#编译器生成了一个Person类的临时对象, 并调用Person类的默认无参构造函数对其初始化.然后对它的属性逐个赋值.
由此可以想到,要使用对象初始化器,则必须保证类中具有一个无参构造函数. 如果我们自定义了一个有参构造函数而把默认的无参构造函数覆盖了, 则需要重新定义一个无参构造函数.

再例如 给List 中添加元素, 在C#3.0 之前我们需要一个个Add 添加, 而现在直接可以利用集合初始化器即可, 编译器会调用Add方法, 一个个地将初始化的内容添加进去.

Copy code

1 class Program2 {3     List<string> newNames = new List<string>4     {5         "A", "B", "C"6     };7 }

Copy code


4,匿名类型
匿名类型顾名思义就是没有知名类型的类型, 通过隐式类型和对象初始化器两种特性创建了一个类型未知的对象, 使我们在不定义类型的情况下可以实现对象的创建,从而减少了类定义过程的代码.

Copy code

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5         //定义匿名类型对象 6         var person = new {Name = "Barry Wang", Age = 25 }; 7         Console.WriteLine("{0} 的年龄为: {1}", person.Name, person.Age); 8  9         //定义匿名类型数组10         var personCollection = new[]11         {12             new {Name = "Barry", Age = 30},13             new {Name = "Brad", Age = 22},14             new {Name = "Tony", Age = 25}15         };16 17         int totalAge = 0;18         foreach (var p in personCollection)19         {20             //下面一行代码证明了Age属性时int类型21             totalAge += p.Age;22         }23 24         Console.WriteLine("所有人的年龄总和为: {0}", totalAge);25         Console.ReadKey();26     }27 }

Copy code

 

 

5,扩展方法
扩展方法, 首先是一个方法, 他可以用来扩展已定义类型中的方法成员.
如下例所示:

Copy code

 1 public static class Extensions 2 { 3     //对string 类扩展,注意this关键字 4     public static void TestStr(this string s) 5     { 6         Console.WriteLine(s.ToString()); 7     } 8     //对int 类扩展 9     public static void TestInt(this int i, string s)10     {11         Console.WriteLine(s + i.ToString());12     }13 }14 class Program15 {16     static void Main(string[] args)17     {18         //使用扩展方法19         string s = "Test Extensions Methods";20         s.TestStr();//调用方法121         Extensions.TestStr(s);//调用方法222         int i = 100;23         i.TestInt("This value is ");24         Extensions.TestInt(i, "This value is ");25         Console.ReadKey();26     }27 }

Copy code

 

并不是所有的方法都可以用作扩展方法, 我们需要考察它是否符合下列扩展方法的定义规则:
(1)扩展方法必须在一个非嵌套, 非泛型的静态类中定义
(2)它至少要有一个参数
(3)第一个参数必须加上this关键字作为前缀(第一个参数类型也称为扩展类型, 即指方法对这个类型进行扩展)
(4)第一个参数不能使用任何其他修饰符(如不能使用ref out等)

Copy code

 1 namespace CurrentNameSpace 2 { 3     //要想使用不用命名空间下的扩展方法, 需要先引入该命名空间 4     using CustomNameSpace; 5     class Program 6     { 7         static void Main(string[] args) 8         { 9             Person p = new Person {Name = "BarryWang"};10             p.Print();//等价于==>ExtensionClass.Print(p);11             p.Print("Hello");12             Console.ReadKey();13         }14     }15 16     //自定义类型17     public class Person18     {19         public string Name { get; set; }20     }21 22     //当前命名空间下的扩展方法定义23     public static class ExtensionClass24     {25         public static void Print(this Person p)26         {27             Console.WriteLine("调用的是当前命名空间下的扩展方法输出, 姓名为: {0}", p.Name);28         }29     }30 }31 32 namespace CustomNameSpace33 {34     using CurrentNameSpace;35     public static class CustomExtensionClass36     {37         //扩展方法定义38         public static void Pint(this Person p)39         {40             Console.WriteLine("调用的是CustomNameSpace命名空间下扩展方法输出: 姓名为: {0}", p.Name);41         }42 43         //扩展方法定义44         public static void Pint(this Person p, string s)45         {46             Console.WriteLine("调用的是CustomNameSpace命名空间下扩展方法输出: 姓名为: {0},附加字符串为{1}", p.Name, s);47         }48     }49 }

Copy code

 

打印结果是:
调用的是当前命名空间下的扩展方法输出, 姓名为: Barry Wang
调用的是CustomNameSpace命名空间下扩展方法输出, 姓名为: Barry Wang, 附加字符串为Hello

注解: 大家看到p.Print(); 是否有些疑惑呢? 对于C#3.0编译器而言, 当它看到某个类型的变量在调用方法时, 它会首先去该对象的实例方法进行chazhao,如果没有找到与调用方法同名并参数一致的实例方法,
编译器就回去查找是否存在合适的扩展方法. 编译器会检查所有导入的命名空间和当前命名空间中的扩展方法, 并将变量类型匹配到扩展类型.
从编译器发现扩展方法的过程来看, 方法调用的优先级别顺序为: 类型的实例方法-->当前命名空间下的扩展方法-->导入命名空间的扩展方法.

解释上面代码打印结果的由来: 在以上代码中存在另个不同的命名空间, 它们都定义了带一个参数的扩展方法Print. 根据执行顺序, 编译器会首先查看Person类型中是否定义了无参的Print实例方法.
如果有, 则停止查找; 否则继续查找当前命名空间下, 即CurrentNameSpace下是否定义了一个带参数的扩展方法Print.
此时在CurrentNameSpace命名空间下, 正好存在带一个扩展类型参数的Print扩展方法, 编译器因此不会再继续查找其他引入命名空间了. 所以p.Print() 调用的是当前命名空间下的Print扩展方法.
而p.Print("Hello")的调用也是一个道理.

 

PS: 终于把今天的两篇博文更新完了, 这么冷的天 真是不容易, 记录到这里希望自己以后有时间还会再看看吧!  

Category:  C# basic seriesreading notes

Good article should  pay attention to my  favorite article  

Is a flower considered romantic

Guess you like

Origin blog.51cto.com/7592962/2543848