孙展波:实现.NET无限潜力

Realize .NET Potential (中文版)
随笔 - 430, 评论 - 12203, 引用 - 54

导航

关于

贴子以"现状"提供且没有任何担保也没有授予任何权利。
计数器(2005/12/8起).点击阅读我的Blog In English

标签

每月存档

最新留言

广告

微软实录之开发提示:foreach隐含了显式的类型转换(explicit type conversion)

如果我们有如下的类定义:

public abstract class FruitBase {

}

 

public class Apple : FruitBase {

}

 

public class Orange : FruitBase {

}

 

public class FruitCollection : System.Collections.ObjectModel.Collection<FruitBase> {

}

 

public class Menu {

    public FruitCollection Fruits { get; set; }

}

对于Menu的一个实例,比如olympicMenu,其Fruits属性可能只含有Apple,但是也许全是Orange,或者是多种FruitBase字类实例的集合。

如果我们还定义了Fruit这样一个类:
public class Fruit : FruitBase {

}

 

这样就很有可能让一些程序员产生错觉了,以为Apple, Orange都是Fruit的子类。而MenuFruits这个名字,似乎也提示其是Fruit的集合,而不是FruitBase的集合。

 

在一个需要对olympicMenu.Fruits中的每个实例进行检查的代码中,如果程序员忘掉了这一点,就可能会写出这样的代码:

 

for (int i = 0; i < olympicMenu.Fruits.Count; i++) {

    Fruit f = olympicMenu.Fruits[i];

//...Op on f

}

 

幸运的是,编译器会给出这样的错误信息:Cannot implicitly convert type ‘FruitBase’ to ‘Fruit’. An explicit conversion exists (are you missing a cast?)

 

因为explicit必须显示写出,程序员的错误得以避免。但是,foreach显然是要比for循环更简练。如以下的代码所示:

 

foreach (Fruit f in olympicMenu.Fruits) {

//...Op on f

}

这个代码却不再会得到编译器的错误提示了!foreach隐含了显式的类型转换!编译通过!如果在调试运行时olympic.Fruits的元素都是Fruit类型,这个错误也不能在调试时发现。这样发布的产品就有了这样一个bug。在运行时,如果olympic.Fruits含有Orange, Apple类的实例时,InvalidCastException将会出现。

 

FruitBase这个抽象类,就这样又被人记起了。

 

===

写这个blog,是因为今天分析了一个在已经发布的SP1中的bugVS 2008 SP1的代码中并没有FruitBase, Fruit, Apple, Orange, 但与之相对应的有WPF中的:TriggerBase, Trigger, MultiTrigger, DataTrigger.

 

这行代码,

foreach (Trigger trigger in curStyle.Triggers)

导致了一个需要通过发布补丁的大bug. 成本是很大的。如果Trigger的命名是PropertyTrigger,不但可能避免这个混淆,而且也更加精确地描述了其功用。

 

回想几年前WPF还在开发的时候,我就提出过这一意见。当然有支持的声音。但是最后还是用Trigger这个名字了。其最有力的理由是什么呢?

 

且听微软实录下一篇。

posted on 2008-09-06 08:32:43 by zhanbos  评论(0) 阅读(3945)

Type类的Name, FullName和AssemblyQualifiedName

一个类型的FullName是能够在一个Assembly(程序集)里唯一限定的名字,所以至少要有名称空间(Namespace)的信息。而一个类型的AssemblyQualifiedName则还要唯一限定了Assembly的名字。

 

如果Type t = typeof(bool);Name, FullNameAssemblyQualifiedName分别是

Boolean

System.Boolean

System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

 

如果Type t = typeof(Nullable<bool>);呢?能不能设想其FullName会是什么样的?

 

你会注意到其FullNamebool类型显示的是AssemblyQualifiedName。不能只使用boolFullName,虽然System.Boolean是在其定义的assembly(即mscorlib)中唯一指定了此类型,在别的Assembly中也可能有另外一个类有着相同的FullName。所以,为了能够在一个Assembly中(也是mscorlib)唯一限定Nullable<bool>, bool必须使用AssemblyQualifiedName才正确。

 

如果Type t = typeof(Nullable<bool>);Name, FullNameAssemblyQualifiedName分别是:

 

Nullable`1

System.Nullable`1[[System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

System.Nullable`1[[System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

 

贴子以"现状"提供且没有任何担保也没有授予任何权利

posted on 2005-05-30 04:53:00 by zhanbos  评论(4) 阅读(4123)

实现 Interface的函数总是虚函数么?

以下这段代码有编译错误:

  public interface IExample

  {

    void ShowExampe(string example);

  }

  public class DetailedExample : IExample

  {

    public void ShowExampe(string example)  //Line 7

    {

      //implementation detail

    }

  }

  public class BlogDetailedExample : DetailedExample

  {

    public override void ShowExampe(string example)   //Line 14

    {

      //My override

    }

  }

 

错误代码是CS0506: 'function1' : cannot override inherited member 'function2' because it is not marked "virtual", "abstract", or "override"。从此我们也许可以推断出第七行的ShowExample不是虚函数。实际上,它是虚函数,至少从IL代码可以看到virtual的标记:
method public hidebysig newslot virtual final instance void ShowExampe(string example) cil managed

 

但是我们注意到了final的使用,这使得此虚函数不可以再被override. 现在将第七行改成 public virtual void ShowExampe(string example) 则可以使得代码编译通过。此时其IL就和类自己定义的(而不是通过实现interface得到的)虚函数一样了:
.method public hidebysig newslot virtual instance void ShowExampe(string example) cil managed

 

当然我们也可以将类DetailedExample改成abstract(抽象)类也使得编译成功,这也和类自己定义的其他抽象函数一样。

  public abstract class DetailedExample : IExample

  {

    public abstract void ShowExampe(string example);

  }

ShowExampleIL代码如下:
.method public hidebysig newslot abstract virtual instance void ShowExampe(string example) cil managed

 

所以编译器只是对于未使用virtual或者abstractinterface函数实现做了特别的处理,使得它是虚函数,但是final的函数实现所以不可以再被override. 回答标题的问题,答案是肯定的。

 

C#之中,标记一个虚函数为final的关键字是sealed。如第十四行就可以这样写.

public sealed override void ShowExampe(string example)

 

贴子以"现状"提供且没有任何担保也没有授予任何权利

posted on 2004-11-11 13:49:00 by zhanbos  评论(16) 阅读(6503)

C#对局部变量的声明更严格

C/C++下合法的语句:

 

  int test = 1;

  if (test != 0)

  {

       char test = 'a';

  }

 

却不能在C#下编译,得到CS0136的错误: A local variable named 'test' cannot be declared in this scope because it would give a different meaning to 'test', which is already used in a 'parent or current' scope to denote something else,

 

C#规范8.5.1之中对此作了明确的规定:Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name.                    

 

所以C#对局部变量的声明更严格。而之所以有这样的规定,我想主要目的是防止编程的无意之中的错误。

 

贴子以"现状"提供且没有任何担保也没有授予任何权利

posted on 2004-10-26 11:49:00 by zhanbos  评论(11) 阅读(7716)

DEV322课程补充材料(2): 范型性能演示代码

在DEV322课程上使用的范型性能演示代码可以在此处得到。这是一个Console Project。

代码还使用了anonymous method的用法。

posted on 2004-09-18 06:03:00 by zhanbos  评论(20) 阅读(5269)

.NET Quiz第17 期第3题

排行榜在此.

?

这里是周末休闲之.NET Quiz微软技术教育大会Distributed版本。TechEd期间每天发布时间不能固定。正确答案未必能够及时回复。

?

今天我在从广州到上海的转换之中用公共计算机发的Post.

?

在C#2.0中可以使用以下的代码:

... Global::System.IO.File.Exists(@“C:\boot.ini”) ...

?

请说明::的含义和意义所在。

posted on 2004-09-14 12:09:00 by zhanbos  评论(18) 阅读(4048)

Partial Type

ASP.NET 2.0和Avalon都使用了Partial Class. 这样用户的代码和工具生成的代码可以共同组成一个Class.对Partial Type的支持来自编译器. 4个要点如下:

  1. 我们把partial 叫做modifier而不是关键字.
  2. 同一类型在不同文件里面都需要使用partial
  3. 可以只在一个文件,两个文件,或者更多文件中使用partial
  4. Base class只需要声明一次;若多次声明必需完全一致.

例子:
Contact1.cs
using System;

namespace Realize.Net.Potential.Sample1

{

  public partial class Contact

  {

    public Contact(string firstName, string lastName)

    {

      this.firstName = firstName;

      this.lastName = lastName;

    }

    int partial = 0;

    private void PartialIsNotAKeyword()

    {

      partial++;

    }

  }

  public partial class AdvancedContact: Contact

  {

    public AdvancedContact(string firstName, string lastName, string address) : base(firstName, lastName)

    {

      this.address = address;

    }

  }

}

Contact2.cs

using System;

namespace Realize.Net.Potential.Sample1

{

  public partial class Contact

  {

    string firstName;

    string lastName;

  }

}

Contact3.cs

using System;

namespace Realize.Net.Potential.Sample1

{

  public partial class Contact

  {

    public virtual string Name

    {

      get { return firstName + " " + lastName; }

    }

  }

  public partial class AdvancedContact

  {

    string address;

    public override string Name

    {

      get

      {

        return base.Name + " at " + address;

      }

    }

  }

}

MainFun.cs

using System;

namespace Realize.Net.Potential.Sample1

{

  public class MainFunc

  {

    [STAThread]

    static int Main (string[] args)

    {

      Contact contact = new Contact("Zhanbo", "Sun");

      Console.WriteLine(contact.Name);

      AdvancedContact advContact = new AdvancedContact("Zhanbo", "Sun", " 1 Microsoft Way ");

      Console.WriteLine(advContact.Name);

      return 0;

    }

  }

}

To Compile:

csc /out:MySample1.exe /t:exe *.cs

 

深入了解Generics, iterators, anonymous methods, and partial classes的最佳读本就是更新的C#2.0Specification.

 

贴子以"现状"提供且没有任何担保也没有授予任何权利。 (首次发布在11/11/2003)

posted on 2004-05-25 02:46:00 by zhanbos  评论(91) 阅读(13519)

Whidbey C#: 匿名方法 | Anonymous Method

再一个WinForm中添加对其包含的一个按钮button1的Click事件的event handler,我们在C# V1.x中常常这样写:

this.button1.Click += new System.EventHandler(this.button1_Click);

 

private void button1_Click(object sender, System.EventArgs e)

{

      MessageBox.Show("Button 1 is clicked");

}

 

当然VS.NET的支持让我们不需要手工写出这么多代码。不过当event handler不需要使用sender, e的情况下,能不能只写出event handler的body而无需考虑代理的signature?

 

在C# V2.0,anonymous method提供了这一可能。上面的代码可以改写为:

 

this.button1.Click += delegate

{

      MessageBox.Show("Button 1 is clicked");

};


贴子以"现状"提供且没有任何担保也没有授予任何权利。

posted on 2004-03-06 12:58:00 by zhanbos  评论(19) 阅读(5803)

Whidbey C#: 属性的getter,setter可以有不同的可访问性

C# 1.0版本下,可读可写属性的getter和setter必须有相同的visibility(可访问性)。这不能满足有些情况下的需求。2.0就解决了这个问题。如下面例子所示,Capacty的setter比getter的Visibility要小。

            public int Capacity

            {

                  get

                  {

                        return capacity;

                  }

                  internal set

                  {

                        capacity = value;

                  }

            }

            private int capacity = 0;

智力/知识测试:这样的代码拿到1.0的csc上编译,会得到什么样的输出呢?

posted on 2004-03-04 14:29:00 by zhanbos  评论(120) 阅读(6872)

Powered by: Joycode.MVC引擎 0.5.2.0