巴别塔上的雇工


错觉:C#中一个class可以同时用implict和explicit的方法实现一个interface两次
10月 31, 2008, 11:54 上午
Filed under: 技术体会

下面的C# code你觉得会编译过去吗?

interface IFoo
{
    void DoSomething();
}
 
class Foo : IFoo
{
    #region IFoo Members
    public void DoSomething()
    {
        Console.WriteLine("do something implicitly");
    }
    #endregion
 
    #region IFoo Members
    void IFoo.DoSomething()
    {
        Console.WriteLine("do something explicitly");
    }
    #endregion
}

第一眼看到这样的code,我的判断是编译器会报错,一个类(class)不应该能够继承一个接口(interface)两次,不然的话就乱套了,像上面的code,当调用Foo对象的DoSomething函数的时候,到底是输出"do something implicitly"呢,还是输出"do something explicitly"呢?

事实上,上面的code完全能够被编译,下面使用这个类的code得到的输出是第一行"do something implicitly",第二行"do something explicitly"。

Foo f = new Foo();
f.DoSomething();
 
((IFoo)f).DoSomething);

这个现象很让人迷惑,怎么可能呢?C#怎么可能容许一个类实现一个接口两次呢?答案是,如果存在explicit implmentation的话,那么你看到的"implicit implementation"实际上就不再是实现这个接口的函数,在上面的例子里,第二个DoSomething函数实现了IFoo中声明的DoSomething,第一个DoSomething和IFoo没有任何关系,只不过碰巧signature相同而已。如果没有Explicit Implementation,那么这个"implicit implementation"就真的是实现IFoo中的DoSomething了。

使用ILDASM工具看产生的IL代码会清楚这一点,下面是第一个DoSomething的IL代码。

.method public hidebysig instance void  DoSomething() cil managed
{
  // Code size       13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "do something implicitly"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ret
} // end of method Foo::DoSomething
 
如果我们把第二个DoSomething,也就是explict implementation的DoSomething删掉,产生的IL代码如下,唯一的区别就是函数多了newslot virtual final属性,也就表示这个函数是实现继承的interface的函数。
 
.method public hidebysig newslot virtual final 
        instance void  DoSomething() cil managed
{
  // Code size       13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "do something implicitly"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ret
} // end of method Foo::DoSomething

总之,这是一个假象,一个错觉,让人感觉C#有一个冒天下之大不韪的特性居然可以让一个类实现一个接口的函数两次。有一些高手前辈解释了一下为什么会有这样一个特性,但是我觉得他们更多的是解释为什么C#需要explicit implementation。

C#的这一“特性”带来一些危险,Jeff Richter在CLR via C#中也指出explicit implementation要小心使用。假如我有一段code是这样:

Foo f = new Foo();
f.DoSomething();

现在我想Refactor它一下,改成

Action<IFoo> func = foo => foo.DoSomething();
func(f);

你觉得这个refactor似乎天衣无缝,但是,实际上程序只想的结果就不同了。



漫画接龙:吴宇森
10月 30, 2008, 10:00 上午
Filed under: 原创卡通

image

最近枯燥的技术性文章写的不少,画却一幅没画,今天终于得闲画一幅。上次是周润发,所以,这次,很自然的,是吴宇森。

吴宇森,我最喜欢的香港导演……之一,且慢,他到底算是香港导演,还是好莱坞导演,让他享誉全球的电影无疑是在好莱坞拍的,但是风格还是从香港电影中起步的,我相信吴宇森还是极力想在好莱坞电影中表现自己的世界观,无奈好莱坞制作不同于香港制作,不是什么都听导演的,现在吴导回归华语电影,也算是意料之中。

吴宇森电影是男人电影,宣扬的是中国传统的道德——义气。我们小时候总被教育不要讲“义气”,讲“义气”的结果就是堕落为黑社会,好孩子都是不讲“义气”的。当然,“义气”很容易被利用,但是这样完全扼杀“义气”,实在是扼杀中国孩子的血性,还好我们有吴宇森这样的人一直孜孜不倦地讲述“义气”地故事。



Generic Variance in C# 4.0
10月 29, 2008, 2:20 上午
Filed under: 技术体会


C# 4.0
10月 28, 2008, 1:13 下午
Filed under: 技术体会


Windows Live ID将支持OpenID,成为OpenID Provider
10月 28, 2008, 1:55 上午
Filed under: 技术体会

Microsoft曾经有一个梦想,让自己的Passport/Live ID成为整个Internet的Single Sign On服务提供者,也就是说所有的用户身份验证都通过微软的服务器。这个梦想被事实证明是一个空想,谁都知道用户群是资源,谁会甘心让自己的用户群让别人去管理呢?

任何有点实力的网络服务提供商都回想自己来管理和控制自己的用户群,但是这样导致的一个问题,一个用户可能既是Yahoo!的用户,也是StackOverflow的用户,但是这两个网站没有任何关系,用户不得不记住两个用户名和密码(最简单常用的身份验证方式),当然,可以两个网站用同一个用户名和密码,但是修改密码就很不方便了,尤其当你是多个网站用户的时候,所以,还是存在对Single Sign On的需求。像Passport/Live ID这样的proprietary protocol不可能获得普遍采用,但是一个open的protocol有可能,所以OpenID诞生也就才情理之中。现在,Windows Live ID也宣布即将支持OpenID了,我觉得算是放弃了最初的梦想。



.NET的新Logo
10月 27, 2008, 2:34 下午
Filed under: 技术体会

在PDC 2008前夕,Microsoft公布了.NET的新Logo,这个新Logo的主题是Wave,好像一个飘舞的……带子,而形状又是明显的字母N,看看现在.NET世界的众多软件, NUnit, NHibernate…… N无疑是代表.NET的一个字母。

net-logo

按照官方的说法,.NET将定位于于Sliverlight和Visual Studio等产品的结合,提供更好的用户体验,所以这个Logo的风格很类似Silerlight的Logo,而且这样的Logo无疑更让人相信.NET能够提供很酷的User Experience。

 silverlight5

从来只见新人笑,不见旧人哭,还是让我们看看已经八岁的.NET老Logo吧。

net-old

在.NET诞生之初,被Microsoft当作一个概念猛炒,什么产品都要带上“.NET”,结果反而让人搞不清什么是.NET,有趣的是,这个老Logo看起来就像一个后缀(Suffix),感觉就是专门用来给任何其他产品贴金用的。时至今日,.NET已经足够成熟而且有了自己明确的方向,所以改一下Logo,也算是自然而然的事情。



Insert Beautified Code in Live Space Blog
10月 25, 2008, 6:41 上午
Filed under: 技术体会

以往我往Blog上写code,为了省事而且让code看起来漂亮一些,就直接在Visual Studio里面截屏贴上来,但是没法让读者把code拷贝出来自己试试。

这个网页介绍了众多方便将code转为HTML的工具,使用这些工具来贴code效果都不错,下面这段code是用Code Snippet plugin for Windows Live Writer写的。

 

public class Program
{
   public static void Main(string[] args)
   {
        Console.WriteLine("This is awesome!");
   }
}


Diversity
10月 25, 2008, 2:28 上午
Filed under: 工作心情

上周就没有写Blog,有两天时间都在参加培训。这次培训聚集了全国各个开发部门的同事,有的同事不远千里从上海赶来,还有的同事不远两千里从深圳赶来,这个培训的交互性比较强,每个人都需要说话表达,两天下来,可以明显感觉到一个公司里面不同的人都有不同的个性,无论是思考的方式还是看做事的方式都不一样,而且这种差异并不是以地域或者部门区分,也就是说,同一个部门的人,都会存在这样的差异。

这就是我们经常说的Diversity(多样性),如果一个公司所有的人只有一种脸孔,或者一种脸孔的人占绝对多数,就很容易走向极端,这可不是好事,容许不同的人都能够存在这个公司,能够达到均衡,才是长久之道。

从个人角度出发,也要认识到Diversity的必要性,这道理也许谁都明白,但是肯定不是谁都能做到,你周围的同事肯定有和你风格不同的,也许你很外向,你的同事很内向;你表现欲强,你的同事沉默寡言;你觉得一个东西是方的,你的同事觉得这个东西是圆的。这些差异在哪里都回出现,不要觉得和你不同就产生排斥心理,包容差异,互相帮助才是正道。

 



Why IEnumerable Can NOT Be Assigned to IEnumerable?
10月 20, 2008, 10:07 上午
Filed under: 技术体会

今天碰到一个有趣的问题,我有一个函数需要接受一个IEnumerable<BaseClass>类型的参数,因为BaseClass实际上是一个abstract class,所以我做了一个子类SubClass继承BaseClass,将一个IEnumerable<SubClass>作为参数传递进去,一切都很自然,但是编译器报了一个错误:

error CS0029: Cannot implicitly convert type ‘System.Collections.Generic.IEnumerable<Subclass> to ‘System.Collections.Generic.IEnumerable<BaseClass>’.

也就是说,C#不容许把IEnumerable<SubClass>直接赋值给IEnumerable<BaseClass>,但是我直观上看,所有适合IEnumerable<BaseClass>的场合,IEnumerable<SubClass>应该都能适合,为什么不容许呢?

C#这么做,当然有道理,而且并不是针对IEnumerable of T,而是针对Generics。也许对于IEnumerable of T,我们期望的行为没有什么问题,但是除了IEnuemrable我们还有别的Collection Type,不如List of T。现在假如C#认为下面的Code合法

List<BaseClass> list = new List<SubClass>;

那么我们得到一个List<BaseClass>类型的list,它实际上是一个List<SubClass>,应该只存储SubClass类型的对象,但是,我们完全可以接着这么写code:

list.Add(new AnotherSubClass());

因为list是类型List<BaseClass>,它有一个成员函数是Add(BaseClass obj),能够接受任何BaseClass类型或者其子类型的对象,像上面的code一样,我们传另外一个子类AnotherSubClass的对象进去,也完全可以。但是,list实际上是List<SubClass>,不能够接受AnotherSubClass,所以运行时会抛出异常。问题出在哪?出在一开始我们容许把一个List<SubClass>的实例赋給List<BaseClass>类型的变量,C#的设计者肯定不希望看到这样的情况,实际上,引入Generics的初衷之一就是做静态的类型检查,在编译阶段就能发现错误的使用不恰当的对象,所以,C#也就这样设计了,AnyCollectionType<SubClass>不能够赋值给AnyCollectionType<BaseClass>。这有时候会带来一些不方便的地方,但是这是一个正确的决定。

深入一点,我们发现对于数组,没有这个问题,也就是说SubClass[]类型的数组可以赋值给BaseClass[]类型的变量,当然,如果你把一个AnotherSubClass类型的对象赋值给数组上某个元素,在运行时会抛出一个exception。为什么数组容许这样呢?有历史原因,C#在诞生之初,为了能够吸引尽量多的程序员,当时比较流行的语言能支持的特性也要尽量支持,毫无疑问,Java在“比较流行的语言”之列,因为Java支持这种数组的赋值方式,所以C#也支持了,这就是原因。



C#: virtual和delegate,哪个慢?
10月 19, 2008, 5:47 上午
Filed under: 技术体会

今天想到一个实现Template Pattern的问题,我们实现Template Pattern一般都是这样,父类里有个函数定义了做一件事情的框架,但是具体做每一步的方式不确定,所以抽象为虚函数(virtual function),在父类里面这些函数可以有缺省实现,也可以没有,子类可以通过override这些函数来定制具体行为。

因为使用了virtual函数,所以是运行时的动态绑定来决定哪个函数(父类中定义或者子类中定义的)被调用,这样肯定要比non-virtual的函数调用慢一点。

image

我想着,如果不用virtual函数,而用delegate会不会快一点。上面的code是标准的template实现,如果改成下面这样,不时用virtual函数,而是把判断函数以一个delegate的形式注入进去。

image

中间的循环纯粹是为了体现出速度差别,结果出乎我开始的意料,后一种方法比前一种方法慢,在我的机器上花费的时间比大概是8:9。

再一想,这个结果也合理,如果delegate的灵活性更强,如果其速度比virtual更快或者相当的话,那么Base Class Librarary里的很多类也不会是现在这么写了。有得必有失,delegate带来了灵活性,但是丧失了一些速度,所以还是要谨慎地使用。

Update: 我的同事指出我的code中有些问题,对于使用virtual function的DoSomthing,只有一个参数,而对于使用delegate的DoSomthing有两个参数,第二种方式因为需要多传递一个参数,在.NET环境中也就是要多一次压栈操作,理论上自然要慢一些。我修改了一下code,将delegate改为data member,而不是通过参数传递,这样效率得到了一点点提高,但是还是没有virtual function快。不过,这种效率的差别实在很小,重复2亿次,时间差不到1秒,所以,实际使用的时候,哪样方便哪样用。