.net framework3.5新特性2:var、初始化、匿名类和扩展方法

本文为原创,如需转载,请注明作者和出处,谢谢!

.net framework3.5新特性1:Lambda表达式

一、用var定义变量

    C#3.0中提供了一种新的声明变量的方式,这就是var。通过这个关键字,在声明变量时就无需指定类型了,变量类型是在初始化时由编译器确定的。代码如下:


var ss = "abcd";
MessageBox.Show(ss.GetType().ToString());

上面的代码将显示System.String,从而证明C#编译器已经将ss编译成了String变量了。而在输出ss后,再输入“.”后,会看到将String类型变量的相应方法和属性也列出来了,因此可以断定,C#ss看成了String类型,而不是Object。所以使用var定义变量同时可以拥有Object和强类型的优点。

     不过大家不要将var看成是javascriptvar,它们的区别是,javascript是弱类型的语言,而且javascript中的变量(也包括用var声明的变量)可以变换类型,如下面的javascript所示:

var s = "abcd";
s
=3;
alert(s);

    上面的代码第一次给s赋了一个字符串,而第二行代码又给赋了一个整数。这样的代码在javascript中没有任何问题。但在C#3.0中,var变量一但被初始化,确定类型后,就无法改变类型了。如下面的代码是无法编译通过的:


var ss = "abcd";
ss 
= 44;

    综上所述,在使用var定义变量时有以下四个特点:

1.        必须在定义时初始化。也就是必须是var s = “abcd”形式,而不能是如下形式:

var s;
= “abcd”;

2.        一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。

3.        var要求是局部变量。

4.        使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样。但笔者建议如果事先知道变量的类型,尽量使用强类型方式来声明变量。否则,就会造成由于大量使用var,而使得开发人员很难断定某个变量是什么类型。这样不利于程序的维护和升级。

虽然var有利有弊,但笔者个人认为,如果将动态语言转换成C#语言,可以考虑使用var来定义变量。这是因为动态语言没有类型,而要将其转换成强类型的C#语言,就必须给变量指定个类型,但事先确定类型是很费劲的,不如将其指定成var,再由C#编译器去确定变量的具体类型。那么如果在转换的过程中,发现动态语言的变量改变了类型,该怎么办呢?这个可以使用第三部分要讲的“匿名类”来解决这个问题。

二、初始化

    如果一个类有public字段,在建立类的对象实例时可以使用下面的代码来初始化这些字段;

public class MyClass
{
    
public String field1;
    
public int field2;
    
public bool field3;
}

MyClass my 
= new MyClass();
my.field1 
= “abcd”;
my.field2 
= 44;
my.field3 
= true;

    C#3.0中提供了一种更简便的方法来初始化这些public变量,代码如下:

MyClass my = new MyClass
{
    field1 
= “abcd”,
    field2 
= 44;
    field3 
=true;
};

    上面的代码的写法有些象带参数的构造方法,但这将不是调用了MyClass的构造方法(因为MyClass并没有带三个参数的构造方法),而只是C#编译器玩的一个魔术。实际上,上面的代码在编译后,仍然和使用传统的初始化字段的方法一样。只是在语法上看起来更简单(至少不用写那么多个my)。要注意的的,使用这种方法初始化,必须是public的字段(不能是protectedprivate或默认修饰符的字段)。

    C#3.0中还改进了对集合类的初始化方式(使其初始化的方式类似于数组)。但遗憾的是,这种初始化方式只支持用泛型的集合类,也就是说,只有实现了System.Collections.Generic.ICollection<T>的集合类才可以使用这种初始化方法。代码如下:

List<string> myList = new List<string> { "data1""data2""data3" };
foreach (string data in myList)
{
    textBox1.AppendText(data);
}

三、匿名类

    C#3.0中提供了一种新的建立类的方法,代码如下:

var my = new
{
    field1 
= "abcd",
    field2 
= 12
};
MessageBox.Show(my.field1);

    C#编译器会自动推断my是一个有两个public字段的类的对象实例。也就是说相当于下面的代码:

public class MyClass
{
    
public String field1;
    
public int field2;
}

var my 
= new MyClass();
my.field1 
= "abcd";
my.field2 
= 25;
MessageBox.Show(my.field1);

    在第一部分讲到如果动态语言在给变量赋值的过程中改变了变量类型,如果将其转换为强类型语言。当然,一种方法是将变量声明成object类型,或是使用匿名类来解决这个问题。代码如下:

var myVar = new
{
    field_string 
= “abcd”
    field_int 
= 12;
};

    然后根据当前这个变量所使用的类型来决定该使用哪个类字段。

四、扩展方法

    这个世界上总是存在着很多奇妙的东西。然而,在这部分所介绍的扩展方法就是其中之一。从字面上看可能读者很难猜透“扩展方法”是什么意思。然而,看了下面的例子,就会感觉到非常的奇妙。


namespace ExtMethod
{
    
public class Class1
    {
        
public String s = "bill";
    }
    
public class Class2 : Class1
    {
    }
    
public static class AnyClassName
    {
        
public static String getName(this Class1 class1)
        {
            
return class1.s + class1.s;  
        }
    }

    
public partial class Form1 : Form
    {
         
        
private void button1_Click(object sender, EventArgs e)
        {
            Class1 c 
= new Class1();
            MessageBox.Show(c.getName());            
            Class2 c 
= new Class2();
            MessageBox.Show(c.getName());            
        }
    }
}

    看到上面的代码,也许很多人会感到奇怪,在Class1Class2中并没有getName方法,怎么在调用时出来个getName方法呢?实际上,这就是扩展方法的用法,从本质上说,扩展方法就是将静态方法(必须声明成static)插入到某个类和其子类中(也就是说,在这些类中可以使用在外部定义的静态方法)。那么要往哪个类中插入呢?这就要在定义静态方法时指定了。大家可以看看getName方法的第一个参数,使用了this关键字,这就表明这个方法是一个扩展方法,后面的类型就是要插入该方法的类,在本例中是Class1,也就是说在Class1及其子类中都可以使用getName方法。上面的调用代码也相当于下面的代码:

Class2 c = new Class2();
MessageBox.Show(AnyClassName.getName(c));

    但使用c.getName可能会更好一些,而且也降低了对静态方法所在的类(AnyClassName)的依赖性。

    在使用扩展方法时应注意以下几点:

1.        扩展方法所在的类名可以是任意合法的类名。

2.        扩展方法所在的类必须和使用扩展方法的代码在同一个命名空间里,否则无法编译通过。

3.        在本例中,Class1Class2只能声明成public,因为AnyClassName被声明为public。如果AnyClassName不加修饰符,Class1Class2也可以不加修饰符,当然,也可以被声明为public。也就是说,Class1Class2必须有比AnyClassName具有更强的访问性。如下面代码所示:

    class Class1
    {
        
public String s = "bill";
    }
    
class Class2 : Class1
    {
    }
    
static class AnyClassName  // 这时如果前面加public是无法编译通过的。
    {
        
public static String getName(this Class1 class1)
        {
            
return class1.s + class1.s;  
        }
    }

4.        如果在Class1Class2中已经有getName方法了,那么Class1Class2中的getName优先级更高。也就是说,扩展方法是无法覆盖原类中的同名(参数名和类型也相同)的方法的。

扩展方法尤其在很多类需要同样的方法,而这些类又无法继承其它类时特别有用。当然,在要对某个类进行扩展,但我们并没有源代码时,扩展方法也可以派上用场。

posted on 2008-06-10 21:35 银河使者 阅读(2404) 评论(28)  编辑 收藏 所属分类: 原创.net新特性

评论

#1楼  2008-06-10 22:56 路过 [未注册用户]

众所周知,象C#、Java这样的语言都是强类型的,也就是说,在使用变量之前,必须事先指定变量的类型。如String s = “abc”。但在有的时候,我们在使用变量时并不知道它的类型,或是可以根据不同的需要,为一个变量赋不同类型的值(如int和string)。这在老版本的C#中,可以将变量声明成object来解决这个问题。但使用object类型的变量,在效率上会有所降低。而且object也没有类型检查。

————————————————————————————
唉,误人子弟啊,使用了var仍然是强类型!!!别在这儿瞎说好不好???正如你所说的,“可以根据不同的需要,为一个变量赋不同类型的值(如int和string)”,你试试这样的代码能不能通过
var i = 1;
i = "abc";

这样的文章害人不浅!!!   回复  引用    

#2楼 [楼主] 2008-06-10 23:10 银河使者      

使用var当然是强类型了。

哥们, 下面的代码当然不能通过了,你自己试试吧
var i = 1;
i = "abc";

会出现错误 “无法将类型“string”隐式转换为“int" ”错误

因为C#是强类型的,而IL也未提供支持弱类型的指令,所以C#不可能提供弱类型的支持。可以将var结构反编译,看看就知道了

如下面的代码:

var i = "abcd"

反编译后的IL是:

// Code size 8 (0x8)
.maxstack 1
.locals init ([0] string i)
IL_0000: nop
IL_0001: ldstr "abcd"
IL_0006: stloc.0
IL_0007: ret


var i = 1; 反编译后是:

// Code size 4 (0x4)
.maxstack 1
.locals init ([0] int32 i)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: ret

上面的代码不是都反编译成强类型了吗?





  回复  引用  查看    

#3楼  2008-06-10 23:13 横刀天笑      

楼主,一楼贴出来的代码是针对你说的:
*********************************
但在有的时候,我们在使用变量时并不知道它的类型,或是可以根据不同的需要,为一个变量赋不同类型的值(如int和string)。这在老版本的C#中,可以将变量声明成object来解决这个问题。但使用object类型的变量,在效率上会有所降低。而且object也没有类型检查。
*************************************
你仔细再读一下,这段话,看看是否有错误   回复  引用  查看    

#4楼 [楼主] 2008-06-10 23:13 银河使者      

实际上,var是在编译时确定类型的。   回复  引用  查看    

#5楼  2008-06-10 23:16 横刀天笑      

嗯,var要求是局部变量,并且编译器可以推断其是什么类型,这个没错,但是你仔细读读你的第一段话,有明显的歧义,容易误导人   回复  引用  查看    

#6楼 [楼主] 2008-06-10 23:19 银河使者      

@横刀天笑
我的意思是解释一下var的需求,这能大家对“为一个变量赋不同类型的值(如int和string)”有一些误会。 其实这只是初始化的意思。 在这里还没有讲到var,大家先不要往var这里想。var是只能赋一次值的。继续往后看吧,都有详细的例子的注意事项。

看来得改一下前面的序言了! ^-^   回复  引用  查看    

#7楼 [楼主] 2008-06-10 23:22 银河使者      

行了,把序言去了,这回不会有歧义了,那一段是我最后加上的,看来是画蛇添足了。下回不加这么多啰嗦了。   回复  引用  查看    

#8楼  2008-06-10 23:48 560889223      

这些内容……在C# 3.0刚发布的时候有一阵风……
建议楼主翻一下以前的老文章,然后在老文章的基础上增加些新鲜的内容……这样文章的内容才不至于重复……   回复  引用  查看    

#9楼  2008-06-11 00:29 Jeffrey Zhao      

--引用--------------------------------------------------
银河使者: @横刀天笑
我的意思是解释一下var的需求,这能大家对“为一个变量赋不同类型的值(如int和string)”有一些误会。 其实这只是初始化的意思。 在这里还没有讲到var,大家先不要往var这里想。var是只能赋一次值的。继续往后看吧,都有详细的例子的注意事项。

看来得改一下前面的序言了! ^-^
--------------------------------------------------------
var只是一个省略的强类型写法而已,为什么不能赋多次值?只要类型一样就可以了,比如:
var i = 1;
i = 2;
完全可以。   回复  引用  查看    

#10楼  2008-06-11 01:10 李涛      

呵呵,写的不错,把有歧异的地方改了,文章看起来就更好了。
加油!期待楼主下一篇。   回复  引用  查看    

#11楼  2008-06-11 08:42 黎叔 [未注册用户]

感觉3.5的语法太乱, 有那么大的必要非得用var么   回复  引用    

#12楼  2008-06-11 08:55 钢钢      

楼主写得很好,期待下文。。。

@路过
--------------------------------------
唉,误人子弟啊,使用了var仍然是强类型......这样的文章害人不浅!!!
--------------------------------------
有那么严重吗? 即使是错误,也不过只是让大家更深的认识一下它的特性而已。 强烈鄙视站着说话不腰疼的匿名者!

  回复  引用  查看    

#13楼  2008-06-11 09:11 路过 [未注册用户]

@钢钢
不要鄙视,大家都是为了技术而已!如果是笔误大家可以原谅,如果是本质上的错误,就要不留情面的指出来!这样才利国利民,最害人的是有像你这样的拍马屁之辈,发现错误也不提出来,一个劲的喊“写的很好,期待下文”?这样真的就很好吗???   回复  引用    

#14楼  2008-06-11 09:14 Windie Chai(笑煞天)      

在只有var的时候,谁都不知道它是什么类型;
在给var赋值的时候,它的类型就已经确定了。
  回复  引用  查看    

#15楼  2008-06-11 09:16 Windie Chai(笑煞天)      

@黎叔
其实个人觉得,var的最大用处在于和lambda表达式搭配使用。
有时候我们用lambda表达式select出来的东西并不是确定类型的,而且只是临时用一下,也没有必要专门写一个类,所以。。。   回复  引用  查看    

#16楼 [楼主] 2008-06-11 09:30 银河使者      

@ 560889223

本文只是对c#3.0的这四个特性做一个总结。如果谁有关于这个四特性的更高级的用法,或是本文没有涉及到的知识点,也可不吝赐教。

@Jeffrey Zhao
我可没说不能多次赋值,下面是我的原话

一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。

只是说不能再赋其它类型的值了,当然,如果同类型的值是完全可以了。
  回复  引用  查看    

#17楼  2008-06-11 09:30 Jeffrey Zhao      

--引用--------------------------------------------------
Windie Chai(笑煞天): @黎叔
其实个人觉得,var的最大用处在于和lambda表达式搭配使用。
有时候我们用lambda表达式select出来的东西并不是确定类型的,而且只是临时用一下,也没有必要专门写一个类,所以。。。
--------------------------------------------------------
var出现的原因不是因为lambda表达式,而是因为匿名类型。   回复  引用  查看    

#18楼 [楼主] 2008-06-11 09:35 银河使者      

@路过

我的以前开头的一段也许并不能说是错误,可能是有措辞上的问题。有一些歧义。另外声明一下,我可从来没说过var是弱类型,或是可以改变类型。 那句“为一个变量赋不同类型的值(如int和string)”,我的原意是在初始化时可以为其赋不同类型的值,并没有再次赋不同类型值的意思。 但这句确实有一些歧义,现在去掉了。大家看看还有哪句有歧义,尽管指出。   回复  引用  查看    

#19楼 [楼主] 2008-06-11 09:36 银河使者      

还有,本文也已指出,应尽量避免使用var。 本文所讲的技术在很大程度上是为了linq而出现的。   回复  引用  查看    

#20楼  2008-06-11 09:37 Windie Chai(笑煞天)      

@Jeffrey Zhao
同意。
可不可以认为有些lambda表达式select出来的结果也是一种匿名类型呢?   回复  引用  查看    

#21楼 [楼主] 2008-06-11 09:45 银河使者      

@(笑煞天)

当然,只要没指定类型名的,都是匿名类型   回复  引用  查看    

#22楼  2008-06-11 09:48 Windie Chai(笑煞天)      

@银河使者
Thanks。   回复  引用  查看    

#23楼  2008-06-11 10:33 anonymous [未注册用户]

文章稍嫌有些过时了。   回复  引用    

#24楼 [楼主] 2008-06-11 11:03 银河使者      

C#3.0现在才刚刚在国内兴起,还有走很长时间。总是有很多初学者在学习它。对于初学者来说,永远不会过时的。但对于.net专家来说,这些东西太简单了。完全没有必要去看。

本文并不是面向所有读者,而只是针对那么并没有掌握这些技术的读者。   回复  引用  查看    

#25楼  2008-06-11 11:48 装配脑袋      

C# 2已经具有从函数调用的参数推测泛型类型参数的能力,现在只是推广到本地变量的声明上。推测才是这个特性的关键,声明或者初始化都是次要的。   回复  引用  查看    

#26楼 [楼主] 2008-06-11 11:59 银河使者      

是的,推测早就有了。推测能力的强弱也标志着编译器是否更智能化。

  回复  引用  查看    

#27楼  2008-06-11 14:38 Sangplus      

文章最后一句话:
+++++++++++++++++++++++++++++++++++++++++++++++++
当然,在要对某个类进行扩展,但我们并没有源代码时,扩展方法也可以派上用场。
+++++++++++++++++++++++++++++++++++++++++++++++++

貌似可以挖掘一些东西,:)   回复  引用  查看    

#28楼 [楼主] 2008-06-11 15:08 银河使者      

@Sangplus

这是扩展方法的重要应用之一。在没有源代码的前提下,在第三方的类中加入方法,使其看上去看这个类本身的方法一样。 这个实现起来很简单。   回复  引用  查看    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-06-10 23:29 编辑过


相关链接:
 


<2008年6月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

导航

统计

公告

我的其他Blog
http://nokiaguy.blogjava.net

与我联系

搜索

 

常用链接

留言簿(4)

我参与的团队

我的标签

随笔分类(124)

随笔档案(59)

相册

专家Blog

积分与排名

最新评论

阅读排行榜

评论排行榜

60天内阅读排行