一点儿很基础但是很容易搞错的小地方

首先声明一下,这一篇是我前两天看完某位(忘记了是谁了)的一篇Blog后有感而写的,如果那位大哥需要我加上引用声明的话请联系我。
这里呢,没有什么高深的东西,只是一些很基础的,但是你可不一定都知道噢。
1)C#里面的方法如果没有加任何的修饰语,那么它到底是protected还是private的呢?
可能是大家都是学贯Java和C#的了,因此会很容易搞混这两者在一些细微地方的区别。在Java里面是protected的,但是在C#里面,它却是private的。看下面的代码:

 1    class Parent
 2    {
 3        void Print()
 4        {
 5            Console.WriteLine("Parent");
 6        }

 7
 8        class Child : Parent
 9        {
10            public void Print()
11            {
12                Console.WriteLine("Child");
13            }

14        }

15
16        static void Main(string[] args)
17        {
18            Child c = new Child();
19            Parent p = c;
20            c.Print();
21            p.Print();
22            Console.ReadLine();
23        }

24    }

最终的输出是:Child Parent。一点儿都不奇怪,我来解释一下。
由于在C#里面方法默认是private的,而private的方法不能是虚方法,因此Parent里面的Print方法和Child里面的Print方法除了名字一样之外没有任何的联系,你就可以认为Parent的Print方法就是PrintParent,Child里面的Print就是PrintChild。关于这一点,可以看一下生成的IL:
  IL_000a:  callvirt   instance void KeyWordTest.Parent/Child::Print()
  IL_000f:  nop
  IL_0010:  ldloc.
1
  IL_0011:  callvirt   instance 
void KeyWordTest.Parent::Print()
IL里面描述得很清楚,c.Print()调用的就是子类里面的方法,而p.Print()调用的就是父类的方法。
您要是还有点儿不太明白,那么我把前面的代码稍微修改一下:
 1    class Parent
 2    {
 3        public virtual void Print()
 4        {
 5            Console.WriteLine("Parent");
 6        }

 7
 8        class Child : Parent
 9        {
10            public override void Print()
11            {
12                Console.WriteLine("Child");
13            }

14        }

15
16        static void Main(string[] args)
17        {
18            Child c = new Child();
19            Parent p = c;
20            c.Print();
21            p.Print();
22            Console.ReadLine();
23        }

24    }
它对应的IL是:
  IL_000a:  callvirt   instance void KeyWordTest.Parent::Print()
  IL_000f:  nop
  IL_0010:  ldloc.
1
  IL_0011:  callvirt   instance 
void KeyWordTest.Parent::Print()
到这里,没有问题了吧。
您要是还是不太懂得话,可能就是您不太晓得IL的callvirt指令。在IL的调用指令里面有call和callvirt两个。call调用的是声明类型的方法,而callvirt调用的则是运行期类型的方法。
第一段代码里面调用c.Print()编译后的IL是
callvirt   instance void KeyWordTest.Parent/Child::Print()
而第二段是
callvirt   instance void KeyWordTest.Parent::Print(),
那么就是说第一段代码里面根本不存在什么多态、override什么的,两个Print方法根本就是没有任何联系的。
还有一点就是:不要觉得虚函数啦、多态啦很神秘似的,其实就是callvirt这个指令搞得鬼。
2)重载是编译器决定还是运行期决定?
啥也不说,咱先看代码:
 1    class Parent
 2    {
 3        public static void Print(Parent p)
 4        {
 5            Console.WriteLine("Parent");
 6        }

 7
 8        public static void Print(Child c)
 9        {
10            Console.WriteLine("Child");
11        }

12
13        static void Main(string[] args)
14        {
15            Child c = new Child();
16            Parent p = c;
17            Print(p);
18            Print(c);
19
20            Console.ReadLine();
21        }

22    }

23
24    class Child : Parent
25    {
26    }
最终的输出是:Parent Child。奇怪吗?不奇怪。因为重载实在编译器决定应该调用哪个方法的。Print(p)在编译期编译器认为它是Parent类型的,所以它将会调用Print(Parent p)方法。
大家知道这一点在编码时的重要性吧,在用到重载的时候,你觉得明明应该调用Print(Child c)的,但是它却不是,这个时候,你就应该想一下是不是出现了这样的错误呢。
3)new的作用和用法
关于new的阻断作用,new和override的区别,网上的文章就很多了,在这里,我想强调的是编译器默认的是new,一定要注意,在你想用override的时候一定要注明了。
posted on 2007-11-03 23:41  Game_over  阅读(2076)  评论(15)    收藏  举报