从零开始学习C# 2

继续《C#入门经典》,Let's go!

方法的重载

重载也是多态的一种体现,其表现形式是方法名相同,但签名不同。所谓签名就是方法名以及参数,换句话说,重载方法就是几个参数不同的同名方法,在调用的时候,编译器根据输入的参数类型选择对应的重载方法。注意的是,返回值不是方法签名的一部分,因此不能定义只有返回值不同的方法。

OverloadSample
 1 int Max(int a, int b)
2 {
3 return a > b ? a : b;
4 }
5
6 double Max(double a, double b)
7 {
8 return a > b ? a : b;
9 }
10
11 double Max(ref double a, double b)
12 {
13 return a > b ? a : b;
14 }
15
16 //不能定义仅在out和ref上有差别的方法
17 //double Max(out double a, double b)
18 //{
19 //}

委托

C++程序猿可能比较熟悉所谓的“函数指针”,委托就类似于函数指针。我的理解是提供了一种像操作变量一样的操作方法的方式,这么说可能比较抽象,让我们看个例子先:

DelegateSample
 1 class DelegateClass
2 {
3 public delegate int ProcessDelegate(int a, int b);
4 }
5
6 class MethodClass
7 {
8 public int Add(int a, int b)
9 {
10 return a + b;
11 }
12
13 public static int Mutiply(int a, int b)
14 {
15 return a * b;
16 }
17 }
18
19 class Program
20 {
21 static void Main(string[] args)
22 {
23 MethodClass method = new MethodClass();
24 DelegateClass.ProcessDelegate addDelegate = new DelegateClass.ProcessDelegate(method.Add);
25 DelegateClass.ProcessDelegate mutiplyDelegate = new DelegateClass.ProcessDelegate(MethodClass.Mutiply);
26
27 Console.WriteLine(addDelegate(10, 10));
28 Console.WriteLine(mutiplyDelegate(10, 10));
29 Console.ReadLine();
30 }
31 }

先看DelegateClass,该类中定义了一个委托,委托定义的语法类似于抽象方法,利用delegate关键字,委托没有方法体。然后我们在MethodClass中定义了两个方法,注意,想方法签名必须和委托定义一致,这样才能使用委托来调用该方法。接着,我们在Main方法中实例化了两个委托,并将MethodClass中的两个方法作为参数分别传入。注意这里的语法,method.Add,MethodClass.Mutiply是不是很类似于属性呢?对委托的实例化是不是也和实例化一个类很类似呢?这就是我之前说的“提供了一种像操作变量一样的操作方法的方式”。实例化之后addDelegate就代表了Add方法,mutiplyDelegate就代表了Mutiply方法,然后就可以像使用普通方法一样来使用委托了。

委托至少在一个地方是非常有用的,当调用者所调用的方法在他编写代码的时候并不能确定的,就需要使用委托,这就是所谓回调,是事件处理的基础。比如由A提供一个委托,B实现一个方法,并传递给A所定义的委托,这样A就可以调用B所实现的方法了。想想点击一个Button的事件处理的方法是谁写的,又是谁在调用你就会明白。我在之后的文章中还会讨论事件处理,到时候会对此再进行研究。

书的第七章分两部分,第一部分主要讲了一些调试的技巧包括断点,条件断点,监视,即时窗口等,十分有用。在这里我就不赘述了,大家最好看图动手,自己尝试一下~第二部分是异常处理,这是一个重要的议题,但感觉和Java十分类似,没什么需要注意的。如果第一次接触的话建议好好看一下,注意异常处理并不是错误处理,而应该把他理解成处理非正常流程的一种很好的方法(一个举烂了的例子,比如在ATM取款的时候发现没钱了,这不是一个正常的流程,但也是十分可能出现的情况,并不是ATM出现错误(故障),只是一种异常的流程而已)。正确的使用异常处理可以极大得增强代码的健壮性。

属性

属性在我看来就是简化了代码,Java中需要给私有变量提供set和get方法,用来安全地访问变量,而在C#中,是利用了属性,看上去不是方法,变得简单了,实际上编译完的MSIL依然是这两个方法,只是我们看不到罢了。

接口

OOP中很重要的概念,不熟悉OOP的好好看看吧。这里主要提一下文中的一个观点,个人感觉比较有用:接口在发布后最好不要修改它,如果修改了接口就会导致之前实现接口的代码不能正常工作。我们应该创建一个新的接口来扩展旧接口,例如包换一个版本号,如X2,这是创建接口的标准方式。

IDisposable接口,提供了Dispose方法。使用这个接口的目的是就是让用户自己释放重要的稀有资源,由于GC的运行时间是不固定的,稀有资源(例如IO,数据库连接等)如果不能及时释放将会导致程序性能下降。在使用完资源之后调用Dispose方法就可以释放资源。

DisposeSample
 1 //Form类实现了IDisposable接口
2 Form form1 = new Form();
3 //进行一些操作, 然后Dispose释放资源
4 form1.Dispose();
5
6 //另一种使用IDisposable的方式
7 //注意括号里必须是实现IDisposable的类
8 using(Form form1 = new Form())
9 {
10 //进行一些操作, 之后C#会自己调用Dispose方法释放资源
11 }

Dispose方法有一种通用的实现方式:

DisposeImplementionSample
 1 public void Dispose()
2 {
3 Dispose(true);
4 //GC调用对象的Finalize方法会损失性能, 因此自己释放了资源就不用GC再次释放了, 执行下面的语句就是为了这个目的
5 GC.SuppressFinalize(this);
6 }
7 // Dispose(bool disposing) 执行分两种不同的情况.
8 // 如果disposing 等于 true, 手动释放
9 // 如果disposing 等于false, 方法已经被终结器 finalizer 从内部调用过
10 protected virtual void Dispose(bool disposing)
11 {
12 if(disposing)
13 {
14 //执行手动释放
15 }
16 }

关于Dispose,GC等内容还很多,听说CLR via C#里面讲得十分详细,等看过之后再与大家分享。

继承

没什么好说的,OOP的基本概念,需要注意的是C#提供了virtual,override和new关键字来处理继承成员(不只是方法,属性字段也可以),他们之间的关系需要搞清楚。只有virtual成员才能被override,如果virtual成员在派生类中有对应的override成员,那么编译器会自动使用派生类的成员,称为多态,否则始终使用基类成员:

InheritSample
 1 interface IPerson
2 {
3 void Hello();
4 void Bye();
5 }
6 class Person : IPerson
7 {
8 public void Hello()
9 {
10 Console.WriteLine("Hello, person");
11 }
12
13 public virtual void Bye()
14 {
15 Console.WriteLine("Bye, person");
16 }
17
18 //如果没有下面两个方法,那么上面两个方法会被编译器当做对接口的实现,称为隐式实现,下面的称为显示实现
19 void IPerson.Hello()
20 {
21 Console.WriteLine("Hello, iperson");
22 }
23
24 void IPerson.Bye()
25 {
26 Console.WriteLine("Bye, iperson");
27 }
28 }
29
30 class A : Person
31 {
32 //隐藏了基类Person的Hello,VS会提示使用new来显示隐藏
33 public void Hello()
34 {
35 Console.WriteLine("Hello, a");
36 }
37
38 //隐藏了基类Person的Bye,VS会提示使用new来显示隐藏
39 public void Bye()
40 {
41 Console.WriteLine("Bye, a");
42 }
43 }
44
45 class B : Person
46 {
47 public new void Hello()
48 {
49 Console.WriteLine("Hello, b");
50 }
51
52 public override void Bye()
53 {
54 Console.WriteLine("Bye, b");
55 }
56 }
57
58 class Program
59 {
60 static void Main(string[] args)
61 {
62 IPerson[] people = new IPerson[3];
63 people[0] = new Person();
64 people[1] = new A();
65 people[2] = new B();
66 foreach (IPerson person in people)
67 {
68 person.Hello();
69 person.Bye();
70 }
71 foreach (Person person in people)
72 {
73 person.Hello();
74 person.Bye();
75 }
76 Console.ReadLine();
77 }
78 }

一些补充

补充一下,之前一篇文章提到的partial类,其实还有partial方法,partial方法必须是private,且不能是virtual,override,abstract,new,sealed和extern,最后,partial方法的参数不能是out,但可以是ref。partial方法在一个partial类中定义,但不实现,在另一个partial类中实现,如:

PartialMethod
 1 partial class MyClass
2 {
3 partial void DoSthElse();
4
5 void DoSth()
6 {
7 Console.WriteLine("Do sth start.");
8 DoSthElse();
9 Console.WriteLine("Do sth end");
10 }
11 }
12
13 partial class MyClass
14 {
15 partial void DoSthElse()
16 {
17 Console.WriteLine("Do sth else.");
18 }
19 }

如果删除掉partial方法的实现或者该partial类,那么那句DoSthElse的调用就不会被编译,这就是partial方法用处。在自动生成的代码中,自动生成一个partial方法由用户来实现,这样可以略微提供性能。说道这里,再思考一下为什么partial方法为什么不能有返回值呢?大家可以想一想(提示:如果有的话,在使用返回值的时候会发现什么?)

VS的类图编辑器还不错,轻量级,而且也有反向工程,使用比较简单,图形看上去也比Rose,PowerDesigner之流漂亮,而且在VS里集成,用不着再开一个进程啦~

嗯,差不多就这样了,希望大家多提意见,有问题也可以一起讨论,多多交流。

posted @ 2011-10-01 17:15  晓胖  阅读(544)  评论(4)    收藏  举报