代码改变世界

谈谈”隐藏类型局部变量、扩展方法、对象与集合初始化器、匿名类型“

2011-11-23 16:52  哒不溜  阅读(1009)  评论(0编辑  收藏  举报

今天看到李建中老师的一个视频关于C#3.0新特性的讲解。本人自己在其中学到了不少东西,下面分享一下我学到的东西:

隐藏类型局部变量

在隐型局部变量声明中,正被声明的局部变量的类型从初始化这个变量的表达式推导得来。当局部变量声明指明var作为类型,并且该范围域(scope)中没有var名称的类型存在,这个声明就称为隐型局部声明。隐藏类型局部变量为我们节省了代码的编写,其实这些苦力都是编译器为我们做了,例如:

            var i = 5;
var h = 23.23;
var s = "C Sharp";
var intArr = new[] { 1, 2, 3, 4 };
var orders = new Dictionary<string, int>();

编译器处理还原为:

            int i = 5;
double h = 23.23;
string s = "C Sharp";
int[] intArr = new int[] { 1, 2, 3, 4 };
Dictionary<string, int> orders = new Dictionary<string, int>();

隐型局部变量声明中的局部变量声明符(declarator)遵从下面这些约束:

1.声明符必须包含初始化器。

2.初始化器必须是一个表达式。初始化器不能是一个自身的对象或者集合初始化器(§)),但是它可以是包含一个对象或集合初始化器的一个new表达式。

3.初始化器表达式的编译期类型不可以是空(null)类型。

4.如果局部变量声明包含了多个声明符,这些声明符必须具备同样的编译期类型。

5.var声明的仅限于局部变量,亦可以用于foreach,using 等语句中

下面是一些不正确的隐型局部变量声明的例子:

            var x;
var y = { 1, 2, 3 };
var z = null;
var zw = 123;
zw = "123";
扩展方法

扩展方法是可以通过使用实例方法语法调用的静态方法。效果上,扩展方法使得用附加的方法扩展已存在类型和构造类型成为可能。

扩展方法是通过指定关键字 this 修饰方法的第一个参数而声明的。扩展方法仅可声明在静态类中。

下面是声明了两个扩展方法的静态类的例子:

    /// <summary>
/// 定义扩展方法的Class
/// 该Class是Static的
/// </summary>
public static class Extensions
{
/// <summary>
/// 定义扩展方法1
/// 注意该方法是Static的
/// 参数的修饰符this
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static int CountLength(this string s)
{
return s.Length;
}

/// <summary>
/// 定义扩展方法2
/// 注意该方法是Static的
/// 参数的修饰符this
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="index"></param>
/// <param name="count"></param>
/// <returns></returns>
public static T[] Slice<T>(this T[] source, int index, int count)
{
if (index < 0 || count < 0 || source.Length - index < count)
{
throw new ArgumentException();
}
T[] result = new T[count];
Array.Copy(source, index, result, 0, count);
return result;
}
}

使用实例方法语法调用扩展方法(在调用扩展方法之前要先引入namespace):

            string s = "123414123423423423";
int i = s.CountLength();//调用扩展方法1

int[] digits = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] a = digits.Slice(4, 3);//调用扩展方法2
for (int n = 0; n < a.Length; n++)
{
Console.WriteLine(a[n]);
Console.ReadKey();
}

扩展方法允许我们在不改变源代码的情况下扩展(即添加)现有类型中的实例方法。

扩展方法的要点:

1.扩展方法的本质为将实例方法调用在编译期改变为静态类中的静态方法调用

2.注意扩展方法的优先级:现有实例方法优先级最高,其次为最近的namespace下的静态类的静态方法,最后为较远的namespace下的静态类的静态方法

3.扩展方法是一种编译时技术,注意与反射等运行时技术进行区别,并慎重使用

对象与集合初始化器

第一:对象初始化器

对象初始化器由一系列成员初始化器组成,封闭于{和}标记内并且由逗号间隔。每个成员初始化器必须指出正被初始化的对象的域或属性的名字,后面是等号”=”和表达式或者对象或集合的 初始化器。
在等号后面指定表达式的成员初始化器作为与对域或属性赋值同样的方式处理。

举个例子给大家看看:

    /// <summary>
/// 一个有两个坐标的Point
/// </summary>
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}

/// <summary>
/// 由两个points构成的rectangle
/// </summary>
public class Rectangle
{
public Point P1 { get; set; }
public Point P2 { get; set; }
}

public class Rectangle2
{
Point p1 = new Point();
Point p2 = new Point();

public Point P1 { get { return p1; } }
public Point P2 { get { return p2; } }
}

class Program
{
static void Main(string[] args)
{

var a = new Point { X = 0, Y = 1 };
/*
* 它等效于
* var a=new Point();
* a.X=0;
* a.Y=1;
*/
///////////////////////////////////////////////////////////////////////

var r = new Rectangle
{
P1 = new Point { X = 1, Y = 2 },
P2 = new Point { X = 2, Y = 3 }
};
/*
* 它等效于
* var r=new Rectangle();
* var _p1=new Point();
* _p1.X=0;
* _p1.Y = 1;
* r.p1=_p1;
* var _p2=new Point();
* _p2.x=2;
* _p2.y=3;
* r.p2=_p2;
* 这里_p1和_p2是临时变量且是不可见和不可访问的。
*/
///////////////////////////////////////////////////////////////////////

var r1 = new Rectangle2
{
P1 = { X = 0, Y = 1 },
P2 = { X = 2, Y = 3 }
};
/*
* 它等效于
* var r=new Rectangle();
* r.P1.X=0;
* r.P1.Y=1;
* r.P2.X=2;
* r.P2.Y=3;
*/
///////////////////////////////////////////////////////////////////////
}
}

代码中都有详细的解释。

第二:集合初始化器
集合初始化器由一系列元素初始化器组成,封闭进 { 和 } 标记内,以逗号间隔。每个元素初始化器指定一个将被加进正被初始化的集合对象中的元素。
下面是对象创建表达式的例子,包含有一个集合初始化器:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
被应用了集合初始化器的集合对象必须是 实现了正好一个类型 T 的System.Collections.Generic.IConlection<T> 的 类型。此外必须存在从每个元素类型到T类型的隐式转型。
如果这些条件都不满足,就产生编译期错误。集合初始化器对每个指定元素依次调用ICollection<T>.Add(T)方法。
下面的类表示一个名字和电话号码列表的contact。

    public class contact
{
string name;
List<string> phoneNumbers = new List<string>();
public string Name { get { return name; } set { name = value; } }
public List<string> PhoneNumbers { get { return phoneNumbers; } }
}
class Program
{
static void Main(string[] args)
{
var contacts = new List<contact>{ new contact{
Name="Chris Smith",
PhoneNumbers={"111-222-3333","222-333-8888"}
},
new contact{
Name="Zhang Wei",
PhoneNumbers={"650-789-8888"}
}
};
var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };

/*
* 它等效于
* var contacts = new List<contact>();
* var_c1=new contact();
* _c1.Name="Chris Smith";
* _c1.phoneNumbers.Add("111-222-3333");
* _c1.phoneNumbers.Add("222-333-8888");
* contacts.Add(_c1);
* var _c2 = new contact();
* _c2.Name="Zhang Wei";
* _c2.PhoneNumbers.Add("650-789-8888");
* contacts.Add(_c2);
* 这里_c1和_c2是临时变量,不可见,也不可访问。
*/
}
}

对象与集合初始化器要点:

1.对象初始化器实际上利用了编译器对对象中对外可见的字段和属性进行按序赋值

2.集合初始化器会对初始化器中的元素进行按序调用ICollection<T>.Add(T)

3.注意对象初始化器和集合初始化器中成员的可见性和调用顺序。

4.对象与集合初始化器同样是一种编译时技术

匿名类型

匿名类型的名字是由编译器自动产生的,在程序正文中不可被引用。
在同样的程序中,以相同顺序指定了一系列相同名字和类型的两个匿名对象初始化器将会产生相同匿名类型的实例。(这个定义包含了属性的次序,是因为它在某些环境中这是可观测和重要的,比如reflection)

例如:

            var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };

注意:

1.可以使用new关键字调用匿名初始化器创建一个匿名类型的对象
2.匿名类型直接继承自System.Object

3.匿名类型的成员是编译器根据初始化器推断而来的一些读写属性

自己动手写了一个小Demo,供大家参考,有什么问题大家可以给我留言!