撰写了文章 发布于 2020-02-27 21:36:57
从C#开始的编程入门——重载
比如现在有一个用来付款的方法。 如果我们用现金支付:
void Pay(float cash){}
如果我们用银行卡支付:
void PayWithCard(string cardNumber, string password){}
又或是用支付宝:
void PayWithAlipay(Image alipayQr){}
如果有一万种支付方式我们怕不是要取一万个方法名称。
方法重载(overload)解决的问题之一就是让一个方法可以有参数不同的版本。
如你所见,如果不用重载,写作不同名称的函数也没有太大的问题,不过有重载的加持我们可以把功能类似的函数更好地组织在一起。像C++和cs这样的语言,重载函数需要严格区分开来。而一些不支持重载的语言采用了更复杂的函数签名。比如Objective-C和Swift。参数除了类型和名称,还有一个label。在调用时需要提高label来标记参数,并且会被视作函数签名的一部分。比如iOS的UIKit中的一个方法:class func systemButton(with image: UIImage,target: Any?, action: Selector?)。前面的with、target、action就是label。其优点在于调用时可以更好地展示出参数的含义,并且一个函数调用会像一句话一样。
重载方法
简单来说就是同一个方法名称可以写出有不同方法签名的多个版本。它们之间的参数个数、参数类型、返回类型各不相同。(如果这些东西都相同那么便是同一个方法了)。
使用重载,我们就可以把上面这一系列方法写成一组名称相同但参数列表不同的方法。
void Pay(float cash){}
void Pay(string cardNaumber, string password){}
void Pay(Image alipayQr){}
但是需要注意的是,访问级别、ref、参数默认值、参数名称、返回值类型不能被当作区分不同函数的标志。
道理其实很简单。在调用某个有多个重载版本的方法时,各个版本必须能够相互区分开来,才能在调用时确定究竟调用哪个版本。由于cs的方法签名不包括参数名称,调用时也不要求标注参数名称,所以参数名称无法区分不同重载版本。而如果参数列表相同但是返回值类型不同,调用时我们仍然会传递相同的参数,因此也无法区分开来。
重载构造函数
构造函数虽然特殊,但它也是函数,也可以进行重载。
class Foo
{
public int Number { set; get;}
public string Str { set; get; }
public Foo(int number) { Number = number; }
public Foo(string str) { Str = str; }
}
这样在我们构造新实例时便可以用我们想要使用的版本进行构造。
构造函数代理
除了可以把子类的构造函数交给父类,还可以把参数代理给本类的其他构造函数。(我没找到微软是怎么叫这种操作的,我姑且把它叫做构造函数代理)。
public Foo(): this(1){ Str = “Whatever"; }
和base相对应,需要直接调用本类的其他构造函数时,使用this关键字。
运算符重载
如果我们把基本的运算符按照方法的思路写出来。
int Add(int left, int right) { return left + right; }
这些基本的二元算术运算符,无一例外的都是接收两个同类型参数,执行定义的运算,然后返回结果。(当然这个例子实际上是有问题的,因为我在用加法解释加法)这些有特殊符号表示的运算符不外乎是这种方法的特殊语法。
向量是线性代数中的一种基本的数据结构。简单来说,一个二维向量是有x和y两个分量的量,我们可以在直角坐标系中表示一个向量。
struct Vector2
{
public double X { set; get; }
public double Y { set; get; }
}
对于向量的加法,是这样定义的。
public static Vector2 Add(Vector2 left, Vector2 right)
{
return new Vector2{ X=left.X+right.X, Y=left.Y+right.Y};
}
如你所见,虽然加法不一定是实数特有的概念,但是对于不同的数据来说,加法有不同的定义。这样的情况,正适合重载。而我们要做的,是重载加号一类的运算符的行为,让我们可以继续使用加号这种直观的符号,但是有符合数据结构自身的行为。
重载运算符只需要在结构或者类中定义一个符合运算规则的静态方法即可。方法名是operator加上运算符的符号。对于向量加法,我们这样定义。
public static Vector2 operator+(Vector2 left, Vector2 right)
{
return new Vector2(){X=left.X+right.X, Y=left.Y+right.Y};
}
这样,当我们尝试使用+号操作两个向量时,就会调用我们重载的这个方法来做向量加法。
var vec = new Vector2(){X=1,Y=1};
var vec1 = new Vector2(){X=2, Y=2};
var vec2 = vec + vec1;
cs中的大多数运算符都可以重载,包括下标运算符。更加详细的有关运算符重载的内容,可以参见这里。
试一下:试着自己重载一下Vector2结构的==运算符以判断向量相等。
下一篇文章里,我们介绍一个听起来和重载很像的概念,重写。这是实现多态的重要特性。
