1、自动属性
之前的实现方式:
private int id; public int Id { set {id = value;} get {return id;} }
现在可通过自动属性实现: public int Id{ get; set; }
2、推断类型(var):推断类型,又名隐式类型,使用var关键字,可以是内置类型、匿名类型、用户定义类型或 .NET Framework 类库中定义的类型。
从 Visual C# 3.0 开始,在方法范围中声明的变量可以具有隐式类型 var。隐式类型的本地变量是强类型变量,但由编译器确定类型。
下面的两个 i 声明在功能上是等效的:
var i = 10; // implicitly typed
int i = 10; //explicitly typed
使用 var 声明局部变量的各种方式:
// i is compiled as an int var i = 5; // s is compiled as a string var s = "Hello"; // a is compiled as int[] var a = new[] { 0, 1, 2 }; // expr is compiled as IEnumerable<Customer> // or perhaps IQueryable<Customer> var expr = from c in customers where c.City == "London" select c; // anon is compiled as an anonymous type var anon = new { Name = "Terry", Age = 34 }; // list is compiled as List<int> var list = new List<int>();
小结:
[1]变量必须在声明的同时被初始化,编译器要根据初始化值推断类型;初始化表达式不能是null;例如不能用var a; 或 var a = null;声明。
[2]该类型在第一次声明时已具有类型。例如:var a=1; a=""; //报错,因为前面var a=1;已经推断出a为整形了,所以不能把字符串赋值给整形
[3]var 变量不能作为全局变量使用,被声明的变量是一个局部变量,而不是静态或实例字段;
[4]初始化不是一个匿名函数;
[5]语句中只能声明一次变量,声明后不能更改类型;
[6]赋值的数据类型必须是可以在编译时确定的类型;
3、参数默认值
C#4.0的新特性:可选参数、命名参数、参数数组。
[1]、可选参数,是指给方法的特定参数指定默认值,在调用方法时可以省略掉这些参数。
但要注意:
(1)可选参数不能为参数列表的第1个参数,必须位于所有的必选参数之后(除非没有必选参数);
(2)可选参数必须指定一个默认值,且默认值必须是一个常量表达式,不能为变量;
(3)所有可选参数以后的参数都必须是可选参数。
[2]、命名参数, 是指通过命名参数调用,实参顺序可以和形参不同。
[3]、参数数组,通过关键字params定义参数数组。调用时可以传入个数不同的实参,具备很好的灵活性。
实例如下:
//可选参数
void DefaultPara(int age=10,string name="Jack") { Console.WriteLine(age+":"+name); } 测试: defaultPara(); //输出10:Jack defaultPara(15); //输出15:Jack defaultPara(20,"Mary"); //输出20:Mary
注意:只给name参数时,应用参数名;例如DefaultPara(name:"John"); //输出10:John;
//可选参数 static int Add(int a, int b = 2) { return a + b; } //参数数组,关键字params static int Add(params int[] p) { int sum=0; foreach (int i in p) sum += i; return sum; } static void Main(string[] args) { //可选参数 Console.WriteLine(Add(1)); //b=1 Console.WriteLine(Add(1, 3)); //a=1, b=3 //通过命名参数调用,实参顺序可以和形参不同 Console.WriteLine(Add(b:6, a:1)); //调用使用了参数数组的方法 Console.WriteLine(Add(1, 3,5)); Console.WriteLine(Add(1, 3, 5,7)); Console.ReadKey(); }
4、对象、集合初始化器
public class User {
public string name{ get; set; }
public int age{ get; set; }
}
旧实现方式:User user = new User(); user.name = "Jack"; user.age = 18;
[1]对象初始化器:User user = new User(){name:"Jack",age:18};
[2]集合初始化器:List<User> list = new List<User>(){new user(){name:"Jack",age:18}, new user(){name:"Mary",age:21}}
[3]数组初始化器:string[] str = {"123", "456", "789"}
5、匿名类:属性名字或顺序不同生成不同的泛型类,属性为只读
[1]可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。 类型名由编译器生成,并且不能在源代码级使用。 每个属性的类型由编译器推断。
public class User {
public int Age{ get; set; } public string Name{ get; set; }
} //初始化对象 User user0 = new User { Age = 18, Name = "Jack" };
User user1 = new User() { Age = 16, Name = "Mary" }; //用匿名类 var user2 = new { Age = 18, Name = "John" };
可以看到new后面没有跟类类型,此定义了一个匿名类,不需要知道这个类的名字,只能读取属性,不能改写属性值
例如: string name = user.Name;//正确 user.Name = "Jam";//错误
[2]如果两个或更多个匿名类型在同一程序集中具有相同数量属性和属性类型,在相同的顺序中,则编译器会将这些匿名类型视为同一的类型。 它们共享同一编译器生成的类型信息。即
(1)如果另一个 匿名类对象 的属性类型和顺序 一样,那么会共用同一个匿名类!
var user3= new { age = 19, name = "John0" }; Console.WriteLine(user3.GetType() == user2.GetType());//true
(2)如果另一个匿名类对象的属性名称和顺序一样,但类型不一样,那么还是使用相同的匿名类,创建对象时传入不同的类型参数
var user4= new { age = "19", name = "John1" }; Console.WriteLine(user4.GetType()==user2.GetType());//true
(3)如果属性名相同,类型相同,但是顺序不同,那么也会重新创建一个匿名类!
var user5 = new { name = "John2", age = 19 }; Console.WriteLine(user5.GetType() == user2.GetType());//false
6、匿名方法:匿名方法只是在我们编写的源代码中没有指定名字而已,其实编译器会帮匿名方法生成一个名字,然而就是因为在源代码中没有名字,所以匿名方法只能在定义的时候才能调用,在其他地方不能被调用(匿名方法把方法的定义和方法的实现内嵌在一起),
在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法。C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方法,作为编写内联代码的首选方式。
//声明委托 delegate void Printer(string s); class TestClass { static void Main() { // 使用匿名方法实例化委托 Printer p = delegate(string j) { System.Console.WriteLine(j); }; // 调用委托 p("The delegate using the anonymous method is called."); // 使用命名方法实例化委托 p = new Printer(TestClass.DoWork); // 调用委托 p("The delegate using the named method is called."); } // 定义命名方法 static void DoWork(string k) { System.Console.WriteLine(k); } } /* Output: The delegate using the anonymous method is called. The delegate using the named method is called. */
7、扩展方法:扩展方法使我们能够向现有类型"添加"方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。即可以对String,Int,DataRow,DataTable等这些类型的基础上增加一个或多个方法,使用时不需要去修改或编译类型本身的代码。
扩展方法的定义规则:
[1]扩展方法类必须为静态类;
[2]拓展方法被定义为静态方法,但它们是通过实例方法语法进行调用的;
[3]它们的第一个参数指定该方法作用于哪个类型(不可为指针类型),并且该参数以 "this" 修饰符为前缀;
[4];第一个参数不可有任何其他修饰符(ref/out);
[5]仅当你使用 using 指令将命名空间显式导入到源代码中之后,扩展方法才位于范围中。
静态方法需类型实例化后方可调用,"this"指实例化对象。
示例如下:
namespace ExtensionMethods { public static class MyExtensions { public static int WordCount(this String str) { return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; } } }
//调用该扩展方法:
using ExtensionMethods;
string s = "Hello Extension Methods";
int i = s.WordCount();
如果你确实为给定类型实现了扩展方法,请记住:
[1]如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用(类型中定义方法优先于扩展方法)。
[2]在命名空间级别将扩展方法置于范围中。(即引入命名空间)
8、系统内置委托:
C#在公共语言运行时(CLR)环境中系统为我们内置了一些常用的委托,包括Action类的委托、Func类的委托、Predicate<T>委托、Comparison<T>委托等等。以上这些委托的命名空间都是System,所属程序集都是 mscorlib.dll。可以直接利用系统内置委托,实例化它们,而不必显式定义一个新委托并将命名方法分配给该委托。
[1]Action类的委托:无返回类型的委托
(1).Action委托 封装一个方法,该方法不具有参数并且不返回值
(2).Action<T>委托 封装一个方法,该方法只有一个参数并且不返回值
(3).Action<T1,T2>委托 封装一个方法,该方法具有两个参数并且不返回值
…… ……
(17).Action<T1,T2,T3,...,T14,T15,T16>委托 封装一个方法,该方法具有16个参数并且不返回值
实例:
static void Main(string[] args) { #region Action<T>委托示例 //需求:打印出整型集合list的元素 List<int> list = new List<int>() { 1, 2, 3, 4, 5 }; //将匿名方法分配给 Action<T> 委托实例 Action<int> concat1 = delegate(int i) { Console.WriteLine(i); }; list.ForEach(concat1); //将 lambda 表达式分配给 Action<T> 委托实例 Action<int> concat2 = (i => Console.WriteLine(i)); list.ForEach(concat2); Console.ReadKey(); #endregion }
总结:Action类的委托最少可以传入0个参数,最多可以传入16个参数,参数类型皆为逆变,并且不返回值。
[2]Func类的委托:
(1).Func(TResult)委托封装封装一个不具有参数但却返回 TResult 参数指定的类型值的方法
(2).Func(T,TResult)委托 封装一个具有一个参数并返回 TResult 参数指定的类型值的方法
(3).Func(T1,T2,TResult)委托 封装一个具有两个参数并返回 TResult 参数指定的类型值的方法
…… ……
(17).Func<T1,T2,T3,...,T14,T15,T16,TResult>委托 封装一个方法,该方法具有16个参数,并返回TResult参数所指定的类型的值
实例:
static void Main(string[] args) { #region Func<T,TResult>委托示例 //需求:查找整型集合list中大于3的所有元素组成的新集合,并打印出集合元素 List<int> list = new List<int>() { 1, 2, 3, 4, 5 }; //将匿名方法分配给 Func<T,TResult> 委托实例 Func<int, bool> concat1 = delegate(int i) { return i > 3; }; var newlist1 = list.Where(concat1).ToList(); //将 Lambda 表达式分配给 Func<T,TResult> 委托实例 Func<int, bool> concat2 = i => i > 3; var newlist2 = list.Where(concat2).ToList(); newlist1.ForEach(i => Console.WriteLine(i.ToString())); newlist2.ForEach(i => Console.WriteLine(i.ToString())); Console.ReadKey(); #endregion }
总结:Func类的委托最少可以传入输入泛型参数(in,逆变) 0个,最多可以传入输入泛型参数(in,逆变) 16个,传入的输出泛型参数(out,协变)有且只有一个,这个类型是此委托封装的方法的返回值类型。
[3]Predicate<T>委托:表示定义一组条件并确定指定对象是否符合这些条件的方法
static void Main(string[] args) { #region Predicate<T>委托示例 //需求:查找整型集合list中大于3的所有元素组成的新集合,并打印出集合元素 List<int> list = new List<int>() { 1, 2, 3, 4, 5 }; //将匿名方法分配给 Predicate<T> 委托实例 Predicate<int> concat1 = delegate(int i) { return i > 3; }; var newlist1 = list.FindAll(concat1); //将 lambda 表达式分配给 Predicate<T> 委托实例 Predicate<int> concat2 = (c => c > 3); var newlist2 = list.FindAll(concat2); newlist1.ForEach(i => Console.WriteLine(i)); newlist2.ForEach(i => Console.WriteLine(i)); Console.ReadKey(); #endregion }
总结:Predicate<T>委托封装一个方法,该方法传入一个类型参数,这个参数是指要比较的对象的类型,此类型参数是逆变,同时接收一个参数(该参数就是要按照由此委托表示的方法中定义的条件进行比较的对象,参数的类型就是传入的类型参数的类型),该方法始终返回bool类型的值。如果该对象符合由此委托表示的方法中定义的条件,则为 true;否则为 false。
[4]Comparison<T>委托:表示比较同一类型的两个对象的方法
static void Main(string[] args) { #region Comparison<T>委托示例 //需求:将整型集合list中的所有元素倒序排列打印出来 List<int> list = new List<int>() { 1, 2, 3, 4, 5 }; //将匿名方法分配给 Comparison<T> 委托实例 Comparison<int> concat1 = delegate(int i, int j) { return j - i; }; //将 lambda 表达式分配给 Comparison<T> 委托实例 Comparison<int> concat2 = (i, j) => j - i; list.Sort(concat1); list.ForEach(c => Console.WriteLine(c.ToString())); list.Sort(concat2); list.ForEach(c => Console.WriteLine(c.ToString())); Console.ReadKey(); #endregion }
总结:Comparison<T>委托封装一个方法,该方法传入一个类型参数,这个参数是指要比较的对象的类型,此类型参数是逆变,同时接收两个同类型的参数(这两个参数就是要比较的两个对象,参数的类型就是传入的类型参数的类型),始终返回int类型的值,即一个有符号整数,指示 x 与 y 的相对值:
(1)负数(小于0):x小于y; (2)零(0):x等于y;(3)正数(大于0):x大于y;