C# 现在都4.0了,惭愧 ,现在才开始学3.0的东西. 看来学习这一系列的技术又将是一个漫漫长旅, 希望自己或每一个刚开始学习C#3.0的朋友一起
坚持走下去...
下面直接从新语法开始慢慢品味吧这些新特性吧...
一、隐式类型化的变量
(1) 、隐式类型化的局部变量
例如:
static void Main()
{
var myInt=0;
var myBool=true; //可以根据初始值自动推断出变量的数据类型 注意关键字 var的使用.
}
可以通过反射来验证:
myInt.GetType().Name; --输出 Int32
myBool.GetType().Name; --输出 Boolean
还可以对基类库中的所有类型使用隐式类型化 包括数组、泛型、自定义类型
var evenNumers=new int[]{1,2,3,4};
var myMinivans=new List
var myCar=new SportsCar();
在foreach中使用关键字 var
foreach(var item in evenNumbers)
{..}
隐式类型化变量的限制:
a. 只能用于方法、属性内部局部变量的声明。
b. 不能使用var类型 作为方法的返回值类型或参数类型,也不能用它来声明类的数据成员。
c. 声明的同时必须赋值. 不可以赋值为null,因为编译器不可能推断出该变量在内存中实际指向的数据类型
注: var myObj=new SportCar(); //编译器已经推断出了它的类型了,所以可以赋值为null
myObj=null; //这样正确
var myInt=0; //隐式类型化得局部变量的值可以作为其他任何变量的值
var aa=myInt;//可以进行传递
(2)、隐式类型化的局部数组
1.使用新的语法来配置数组类型:
var a=new[]{1,2,3,4}; a的类型是 int[]
var b=new[]{1,1.2,2.4}; b的类型是 double[]
var c=new[]{"we",null,"are",null,"family"}; c的类型是 string[]
var myCars=new[]{new SportsCar(),new SportsCar()}; myCars的类型是 SportsCars[]
注意:数组成员必须为同一可推断的类型 如:全部为Int 或 String 或SportsCar类型
最后要注意的问题:
1. 如果仅仅为了进行数据类型的声明而使用关键字 var 意义不大,使得我们阅读 困难.其重要应用场景是LINQ技术中.
通过查询的格式返回动态创建的结果集.
2.类型一旦推定,就不可以为变量赋予一个不同类型的值:
如:
var s="This variable can onlyhold string data!";
s="This is fine...";
s=1234; //错误
二、扩展方法
背景:
一旦一个类型(类,接口,结构,枚举,委托)被定义然后编译进一个.net程序集后,如果要为该类型添加新的成员或更新、删除
成员的唯一方法就是修改类型的定义代码.然后重新编译并更新新的程序集(或者尝试更高级的方法,如:使用命名空间 System.Reflection.Emit
下的类对已编译的类型做运行时的动态改造.
初始印象:
C#3.0的扩展方法,允许现存已编译的类型(如:.net类库中的类)和当前即将被编译的类型(扩展方法的类型)在不需要直接重新编译的情况下获得功能上的扩展.
目标:
当需要使类型支持一系列的成员(为了实现多态)但不能改变原始定义时,扩展方法可以帮我们解决问题.
使用扩展方法,你可以为已编译的类型添加功能,同时这些方法与原始定义分开存放.
限制:
(1),定义扩展方法的类必须为静态类,因此扩展方法也必须为静态方法
(2)、所有扩展方法都需要使用this关键字对第一个参数(仅对第一个参数) 进行修饰.
(3)、扩展方法只可以被内存中正确的实例调用,或者通过其所处的静态类被调用.
下面 我们以扩展.NET类库中已有的类型添加新的功能为例,开始扩展方法的学习
// 编写一个我们自己的扩展工具类 MyExtensions
namespace myTest
{
static class MyExtensions
{
//扩展所有对象都能够显示自身的类型名称和所处程序的程序集能力
public static void DisplayDefiningAssembly(this object obj)
{
Console.WriteLine("{0} lives here:\n\t->{1}\n",obj.GetType().Name,
Assembly.GetAssembly(obj.GetType()));
}
//扩展所有Int类型都支持反转自身的能力 如:123 将返回 321
public static int ReverseDigits(this int i)
{
char[] c=i.ToString().ToCharArray();
Array.Reverse(c);
string newC=new string(c);
return int.Parse(newC);
}
}
static class TestrUtilClass
{
//为Int类型添加一个指定格式的报告自己的扩展方法
public static void Foo(this int i)
{
Console.writeline("{0} called the Foo() method.",i);
}
//重载 接受多个参数
public static void Foo(this int i,string msg)
{
Console.WriteLine("{0} called Foo() and told me: {1}",i,msg);
}
}
}
1.在实例层次上调用扩展方法
using MyTest;
static void Main()
{
int myInt=12345678;
int.DisplayDefiningAssembly(); //实例调用 所有对象都拥有DisplayDefiningAssembly()方法
DataSet d=new DataSet();
d.DisplayDefiningAssembly(); //实例调用 所有对象都拥有DisplayDefiningAssembly()方法
myInt.Foo(); //实例调用 仅Int类型拥有此Foo()方法
myInt.Foo("Intsthat Foo?Who would have thought it!"); //实例调用 仅Int类型拥有此Foo()方法,this声明的参数忽略
bool flag=false;
flag.Foo();//编译错误 //bool类型不拥有此扩展方法.
}
2.静态调用扩展方法
using MyTest;
static void Main()
{
int myInt=12345678;
MyExtensions.ReverseDigits(myInt); //静态调用
TestrUtilClass.Foo(myInt); //静态调用
TestrUtilClass.Foo(myInt,"Ints that Foo? Who would have thought it!"); //静态调用
DataSet d=new DataSet(); //静态调用
MyExtensions.DisplayDefiningAssembly(d);//静态调用
}
值得注意的一点是: 在使用扩展方法的地方 必须导入这个扩展类型的命名空间
三、对象初始化器:
一个对象初始化器仅仅是一个有若干特定值组成的逗号分隔,并且首尾分别使用{}围起来。
初始化对象的几种方式:
1). 使用特性 通过命名属性语法间接为特性赋值
[AttributeUsage(AttributeTargets.Class,Inherited=false,AllowMultiple=false]
public sealed classSomeInterestingAttribute:Attribute
{..};
2). C#3.0 以前的语法
Point first=new Point(); //通过属性初始化对象
first.X=100;
first.Y=200;
Point aa=new Point(20,20);//通过构造函数
3). 新的语法
var aa=new Point {X=30,Y=50};
Point bb=new Point{X=20,Y=30};
A、使用初始化语法调用自定义构造函数
Point finalPoint=new Point {X=30,y=30}; //隐式调用 默认构造函数
Point finalPoint=new Point(){X=30,y=30};//显式调用默认构造函数 //这种写法没有意义不大
Point finalPoint=new Point(PointColor.Gold){X=100,y=300}; //显式调用一个参数的自定义构造函数 并初始化其他成员
B、初始化内部类型
Rectangle myRect=new Rectangle
{
TopLeft=new Point{X=100,Y=100},
BottomRight=new Point{X=200,Y=200}
}
C、初始化集合类型
1)、初始化标准数组
int [] arr={1,2,3,4,5};
2)、初始化List
List
3)、初始化复杂类型的容器
List
{
new Point{X=2,Y=3},
new Point{X=3,Y=4},
new Point{X=4,Y=5}
};
foreach(var pt in myPoints)
{
Console.WriteLine(pt);
}
或 更复杂的
List
{
new Rectangle {TopLeft=new Point{X=10,Y=10},BottomRight=new Point{X=20,Y=30}},
new Rectangle {TopLeft=new Point{X=20,Y=40},BottomRight=new Point{X=30,Y=30}},
};
foreach(var pt in myPoints)
{
Console.WriteLine(pt);
}
四、 匿名类型
如果定义的类只是用于当前的应用程序,而不需要在项目间重用,需要用到匿名类型.
其实匿名类型也就是隐式类型局部变量(Var)关键子和对象初始化语法的一个应用.
下面看具体的例子:
//构建一个匿名对象来表示一个汽车
var myCar=new {Color="Red",Make="BaoMa",CurrentSpeed=50};
//myCar是隐式类型化得(必须这么做),并没有使用强类型化的类定义来构建汽车类型
//使用属性语法获取 和设置对象的每一个变量
myCar.Color="Black"; //赋值
Console.WriteLine("My car is the color {0}.",myCar.Color); //取值
匿名类型的一些细节:
匿名类型都继承与Object,因此我们可以调用 ToString(),GetHashCode(),Equals()或者GetType()方法.
还可以通过反射来验证它们.
需要注意的是:匿名类型的相等语义
如果两个结构和都一样的匿名类型
Equals() 返回 True 比较的是内容是否相等
== 返回 False 因为匿名类型没有重载C#相等运算符。 所以使用==时 比较的是两者的引用
匿名类型还可以包含匿名类型
var purchaseItem=new {TimeBought=DateTime.Now,ItemBought=new {Color="red",make="Saab"}};
小结:
匿名类型应当谨慎使用,尤其在使用LINQ技术时,匿名类型本身的一些限制:
1).匿名类型继承Object
2)、匿名类型不支持事件,自定义方法和自定义重写。
3)、匿名类型是隐式封闭的(sealed)。
4)、匿名类型的创建只使用默认构造函数
五、Lambda表达式
是C#2.0中的匿名方法一个简写方式,使的我们的代码更加简洁.
说起Lambda表达式 还得从委托谈起
(1)、首先看传统的委托使用
//定义在System命名空间下的 public delegate bool Predicate
class Program
{
static void Main(string[] args)
{
var list=new List
Predicate
List
foreach (var item in evenNumbers)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
//自定义检索条件
static bool CallMeHere(int i)
{
return (i % 2) == 0;
}
}
(2)、使用匿名方法
static void Main(string[] args)
{
var list=new List
List
foreach (var item in evenNumbers)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
注意: 使用匿名方法的好处是不用创建委托实例,以及编写一个独立的方法.
(3)、使用Lambda表达式
static void Main(string[] args)
{
var list=new List
List
//也可以这样 var evenNumbers=list.FindAll(i=>(i%2)==0); 使用隐式类型化
foreach (var item in evenNumbers)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
好处:语言更加简洁,而且而且传统的委托语法消失的无影无踪.
深入分析Lambda表达式
Lambda表达式由三部分组成
如:i=>(i%2)==0;
参数列表 i就是参数列表
=>标记
表达式 (i%2)==0就是表达式
Lambda表达式的参数既可以是显示类型化的 也可以使隐式类型化的。
var evenNumbers=list.FindAll(i=>(i%2)==0); //现在参数是隐式类型化的 表示参数i的数据类型是整形,由编译器推断出来
也可以显示声明
var evenNumbers=list.FindAll((int i)=>(i%2)==0); //显示类型化
单个隐式类型化参数的参数列表的括号可以被省略,也可以使用括号
var evenNumbers=list.FindAll((i)=>(i%2)==0);
表达式也可以使用括号包围
var evenNumbers=list.FindAll((i)=>((i%2)==0));
上面的Lambda表达式只关联一条语句,下面请看 用多行语句来定义Lambda表达式
var evenNumbers=list.FindAll(i=>
{
Console.WriteLine("Called by FindAll()!"); //关联多条语句用{}即可
bool isEven=((i%2)==0);
return isEven;
}
);
含有多个或零个参数的Lambda表达式
如:
实例化一个委托,用Lambda表达式 注意有多个参数的Lambda表达式
m.SetMathHandler((msg,result)=>{Console.WriteLine("Message:{0},Result:{1}",msg,result);});
也可以如下:
m.SetMathHandler((string msg,int result)=>{Console.WriteLine("Message:{0},Result:{1}",msg,result);});
如果使用Lambda初始化一个没有参数的委托,可以使用空括号表示表达式的参数列表
ViewSimpleDelegate d=new ViewSimpleDelegate( ()=> {return "Hello Word";});
d.Invoke();
六、自动实现属性
当属性访问器中不需要其他逻辑时,自动实现的属性可使属性声明变得更加简洁。
如:
class Test
{
public double Price{ get; set; }
public string Name { get; private set; } // read-only 只读属性
public int CustID{ get; private set; }
}
注意:自动实现的属性必须同时声明 get 和 set 访问器。若要创建 readonly 自动实现属性,请给予它 private set 访问器。
七、分部方法 再次不做讲述 详查MSDN.