摘自博客园:
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的时候一定要注明了。