C# 3.0 之 对象初始化器

2009年3月22日星期日

C# 3.0 之 对象初始化器

概念

在传统的C#中,我们常用两种方法来对一个类(或者结构体)进行初始化,一是采用带有参数的构造函数,另一种则是不采用构造函数,或者构造函数中并没有实际地对字段进行赋值,而是在申请了类的实例之后,手动对它的公共属性进行赋值。下面是一个二维空间的几何点:



public struct Point

{

private int xPos, yPos;



public Point(int x, int y)

{

xPos = x;

yPos = y;

}



public int X

{

get { return xPos; }

set { xPos = value; }

}



public int Y

{

get { return yPos; }

set { yPos = value; }

}



public override string ToString()

{

return string.Format("[{0}, {1}]", xPos, yPos);

}

}







// 手动指定每个属性

Point firstPoint = new Point();

firstPoint.X = 10;

firstPoint.Y = 20;



// 调用自定义的构造函数

Point anotherPoint = new Point(20, 30);



而采用类初始化器的C# 3.0代码则可以写成下面的样子:



var yetAnotherPoint = new Point { X = 30, Y = 30 };

Point finalPoint = new Point { X = 30, Y = 30 };



其中第一个是隐式类型变量。这里并没有显式调用Point的构造函数,仅仅是将值设给了公共的X和Y属性。在这背后,类型的缺省构造函数被调用,紧跟着将值赋给指定的属性。从这一点上说,最后这两个实例实际上就是第一个实例的简化写法。Nocturne Studio



如果我们将xPos和yPos设定为公共访问,那么我们还可以写出下面的代码(尽管这是很没有道理的做法):Nocturne Studio



var testPoint = new Point { xPos = 100, yPos = 200, X = 300 };



这样做的最后结果是xPos最终是300,而yPos是200。从这点上来看,对象初始化器是从左到右(呃,或者也可以说从上到下)执行的。这段语句如果用传统的方法来写,就是如下的格式:Nocturne Studio



var testPoint = new Point();

testPoint.xPos = 100;

testPoint.yPos = 200;

testPoint.X = 300;



在初始化语法中调用自定义构造函数

前面一个例子中,Point类型初始化时隐式地调用了缺省构造函数。其实我们也被允许直接显式指明使用哪一个构造函数,比如:



Point finalPoint = new Point() { X = 30, Y = 30 };



当然,也可以不调用缺省的构造函数,而是使用自定义的两个参数的那个构造函数,如下所示:



Point finalPoint = new Point(10, 10) { X = 30, Y = 30 };



执行的结果,构造函数参数里的10,10被忽略,最终的实例是xPos=30,yPos=30。在目前的Point结构体定义中,调用自定义的构造函数没什么太大的用处,反倒显得累赘。然而,我们给Point结构体新增加一个允许在调用时指定一个颜色(通过一个叫做PointColor的枚举类型)的构造函数,那么这样的自定义构造函数与初始化语法之组合就显得很好很强大了。我们把Point结构体改造一下:



public enum PointColor

{

Tan,

Indigo,

Navy

}



public struct Point

{

private int xPos, yPos;

private PointColor c;



public Point(PointColor color)

{

xPos = 0;

yPos = 0;

c = color;

}



public Point(int x, int y)

{

xPos = x;

yPos = y;

c = PointColor.Tan;

}



public int X

{

get { return xPos; }

set { xPos = value; }

}



public int Y

{

get { return yPos; }

set { yPos = value; }

}



public override string ToString()

{

return string.Format("[{0}, {1}, Color = {2}]", xPos, yPos, c);

}

}



然后回到Main()方法中,我们尝试下面这段语句:http://and114.blog.hexun.com



Point tanPoint = new Point(PointColor.Tan) { X = 90, Y = 20 };

Console.WriteLine("Value of Point is: {0}", tanPoint);



可以得到如下的结果http://and114.blog.hexun.com









初始化内部类型

这个说起来很绕口,还是用实例来看。我们定义一个Rectangle类,使用Point类型来代表左上角和右下角两个点的坐标:



public class Rectangle

{

private Point topLeft = new Point();

private Point bottomRight = new Point();



public Point TopLeft

{

get { return topLeft; }

set { topLeft = value; }

}



public Point BottomRight

{

get { return bottomRight; }

set { bottomRight = value; }

}



public override string ToString()

{

return string.Format("[TopLeft: {0}, {1}, BottomRight: {2}, {3}]", topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y);

}

}



使用对象初始化语法,我们可以创建一个Rectangle的实例并且将它内部的点设置如下:



Rectangle myRect = new Rectangle

{

TopLeft = new Point { X = 10, Y = 10 },

BottomRight = new Point { X = 200, Y = 200 }

};



这个时候,我们就能够体会到它相对于传统方法的好处了。看看我们原先要做的事情:



Rectangle r = new Rectangle();

Point p1 = new Point();

p1.X = 10;

p1.Y = 10;

r.TopLeft = p1;

Point p2 = new Point();

p2.X = 200;

p2.Y = 200;

r.BottomRight = p2;



嗯,不用我多说什么了吧 ^_^



集合初始化Nocturne Studio

与对象初始化语法类似的是集合初始化。这个语法使得我们可以用简单的数组类型来初始化一个泛型容器(比如List)。特别地,这种语法只能用于实现了ICollection接口的类型,包括我们自己实现的自定义泛型容器,因为使用到了Add()方法来将元素插入到集合中。在这个约束之下,System.Collection命名空间下的容器(比如ArrayList)就不能使用这种新语法,因为它们并没有实现所需的接口。看下面的例子:



// 初始化一个普通数组

int[] myIntArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };



// 初始化一个int型泛型List<>

List myGenericList = new List { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };



// 下面这句是非法的,ArrayList没有实现ICollection接口

ArrayList myList = new ArrayList { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };



把我们前面用到的Point类应用进来,比如:



List myPointList = new List

{

new Point {X = 2, Y = 2},

new Point {X = 3, Y = 3},

new Point {X = 4, Y = 4}

};



foreach (var pt in myPointList)

{

Console.WriteLine(pt);

}



结果如下:Nocturne Studio









如果到现在你还不能理解这么做的好处,那么看看下面这一组语句吧。这里面用到了嵌套的集合初始化器与对象初始化器。当然你可以尝试一下把它用传统方法来改写,呵呵,那感觉保证让你一爽到底 :-)



List myRectList = new List

{

new Rectangle {TopLeft = new Point {X = 10, Y = 10},

BottomRight = new Point {X = 200, Y = 200}},

new Rectangle {TopLeft = new Point {X = 2, Y = 2},

BottomRight = new Point {X = 100, Y = 100}},

new Rectangle {TopLeft = new Point {X = 5, Y = 5},

BottomRight = new Point {X = 90, Y = 75}},

};



foreach (var r in myRectList)

{

Console.WriteLine(r);

}

0 评论:

发表评论