一点儿很基础但是很容易搞错的小地方
首先声明一下,这一篇是我前两天看完某位(忘记了是谁了)的一篇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
}
class Parent2
{3
void Print()4
{5
Console.WriteLine("Parent");6
}7

8
class Child : Parent9
{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()您要是还有点儿不太明白,那么我把前面的代码稍微修改一下:
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是:
class Parent2
{3
public virtual void Print()4
{5
Console.WriteLine("Parent");6
}7

8
class Child : Parent9
{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_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)方法。
class Parent2
{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 : Parent25
{26
}大家知道这一点在编码时的重要性吧,在用到重载的时候,你觉得明明应该调用Print(Child c)的,但是它却不是,这个时候,你就应该想一下是不是出现了这样的错误呢。
3)new的作用和用法
关于new的阻断作用,new和override的区别,网上的文章就很多了,在这里,我想强调的是编译器默认的是new,一定要注意,在你想用override的时候一定要注明了。

浙公网安备 33010602011771号