帮帮我,我负载过重(C#)

作为有关 C# 语言规范漫谈的继续,本月我们将讨论运算符重载的问题。运算符重载(除非特别指明,否则本专栏的其余
部分一律将其简称为“重载”)是指允许用户使用用户定义的类型编写表达式的能力。它允许用户定义的类型与预定义的
类型具有相同的功能。

例如,通常需要编写类似于以下内容的代码,以将两个数字相加。很明显,sum 是两个数字之和。

int i = 5;
int sum = i + j;

如果可以使用代表复数的用户定义的类型来编写相同类型的表达式,那当然是最好不过了:

Complex i = 5;
Complex sum = i + j;

运算符重载允许为用户定义的类型重载(即指定明确的含义)诸如“+”这样的运算符。如果不进行重载,则用户需要编写
以下代码:

Complex i = new Complex(5);
Complex sum = Complex.Add(i, j);

此代码可以很好地运行,但 Complex 类型并不能象语言中的预定义类型那样发挥作用。

任何事情都有特定的时间和场所
运算符重载是一个容易引起误解的语言功能,而且编程人员对待它的态度也大相径庭。一些人认为:用户使用这一功能编
写的程序将令人费解,而且它也不应归于编程语言。另一些人则认为它是一个很不错的功能,在任何地方都可以使用。

这两种观点既包含正确的成分,但也有欠妥之处。应该承认,运算符重载可能会导致编写出的程序令人费解,但根据我的
经验,即使不使用运算符重载,也很可能编写出令人费解的代码。在某些情况下,不使用重载甚至会使代码更加令人费
解。

那些不分场合、随意使用重载的人“确实”在生产令人费解的代码。

在语言中之所以使用重载,是为了在概念上对用户的类或结构进行简化。只有在有助于提高用户所写代码的可读性时,才
能对运算符进行重载。请注意,我们所说的检验标准是“更清晰”,而不是“更简短”。运用了运算符重载的类几乎总是
会使代码变得更简短,但并不能每次都使代码变得更清晰(即可读性更强)。

为了说明这一点,我创建了多个重载示例。您需要仔细阅读这些代码,想一想哪个运算符进行了重载,重载的运算符执行
了什么运算。

测验
1
BigNum n1 = new BigNum("123456789012345");
BigNum n2 = new BigNum("11111");
BigNum sum = n1 + n2;

B
Matrix m1 = loadMatrix();
Matrix m2 = loadMatrix();
Matrix result = m1 * m2;

iii
DBRow row = query.Execute();
while (!row.Done)
{
  Viewer.Add(row);
  row++;
}

IV
Account current = findAccount(idNum);
current += 5;

答案和讨论
1
本示例中,要执行的运算是显而易见的。这种加法只不过是将预定义的类型相加,每个人都明白执行了什么运算,因此在
这个示例中,使用运算符重载很有意义。

B
本示例演示了矩阵如何相乘。从概念上来说,矩阵乘法与常规乘法不完全类似,但它是一个明确定义的运算,因此任何理
解矩阵乘法的人看到这种重载的运算符时,都不会感到惊讶。

iii
本示例中,增量 (++) 运算符进行了重载,它使数据库行向前移至下一行。任何与数据库行有关的事物都不可能使我们理
解这种增量的真正含义,而且,这种增量要执行的运算也不是那么明显。

在这一示例中,重载的使用也没有使代码变得更简单。如果我们转而使用以下代码,情况就好多了:

DBRow row = query.Execute();
while (!row.MoveNext())
{
  Viewer.Add(row);
}

IV
将事物和雇员相加代表什么含义呢?本示例中,选择是一个不错的方法,将其与雇员数相加就会注册雇员。这是一种很糟
糕的运算符重载用法。

原则
何时进行重载的原则是相当简单的。如果用户希望能执行这种运算,那么就应该进行重载。

重载算术运算符
要重载 C# 中的运算符,指定要执行运算的函数就可以了。函数必须在运算所涉及的类型中进行定义,并且至少有一个参
数属于该类型。这样可以防止对 int 的加法或其它奇怪事物进行重载。

为了演示重载,我们将开发一个矢量。矢量可以被认为是从原点到特定二维点的线。可以对矢量执行多种运算。以下是该
类型的粗略定义:

struct Vector
{
  float x;
  float y;

  public Vector(float x, float y)
  {
      this.x = x;
      this.y = y;
  }
}

要实际使用,矢量应支持以下运算:

获取长度
将矢量乘以某个数字
将矢量除以某个数字
将两个矢量相加
将一个矢量减去另一个矢量
计算两个矢量的点积
我们的任务是确定应该如何实现这些运算。

长度
对于获取矢量的长度,似乎没有任何有意义的运算符。长度不会变化,因此将它作为属性是很有意义的:

  public float Length
  {
      get
      {
        return((float) Math.Sqrt(x * x + y * y));
      }
  }

将矢量乘以/除以某个数字
将矢量乘以某个数字是相当常见的运算,并且是用户希望实现的运算。以下是相关代码:

  public static Vector operator*(Vector vector, float multiplier)
  {
      return(new Vector(vector.x * multiplier,
vector.y * multiplier));
  }

应该注意,此处有许多有趣的现象。首先,运算符是 static 函数,因此它必须获取两个参数的值,同时在结果中必须返
回一个新的对象。运算符的名称恰好是“operator”,后面紧跟着要重载的运算符。

除以某个数字的代码与以上代码类似。

将两个矢量进行加减
这是很常见的矢量运算,因此很显然要对它们进行重载。

  public static Vector operator+(Vector vector1, Vector vector2)
  {
      return(new Vector(vector1.x + vector2.x,
vector1.y + vector2.y));
  }

减法的代码与以上代码非常类似。

计算点积
两个矢量的点积是为矢量定义的特殊运算,在预定义的类型中根本无法找到与之相类似的运算。在方程式中,点积通过在
两个矢量之间写一个点来表示,因此它和任何现有运算符都不是精确匹配。点积的一个有趣特征是:它获取两个矢量的
值,但只返回一个简单的数字。

无论是否对该运算进行重载,用户代码都大致相同。第一行显示了正在使用的重载版本,其它行则显示了两个替代版本:

  double v1i = (velocity * center) / (t * t);
  double v1i = Vector.DotProduct(velocity, center) / (t * t);
  double v1i = velocity.DotProduct(center) / (t * t);

此时,它几乎是一个判断调用。我编写的类对“*”运算符进行了重载,以便进行点积运算,但回过头细想一下,我认为这
一代码并不是最合适的代码。

在第一个示例中,velocity 和 center 是矢量这一点并不是很清晰,因此,点积是要执行的运算这一点也不是很清晰(我
在查找一个使用它的示例时,注意到了这一点)。第二个示例很清楚地说明了要执行什么运算,我认为使用该示例中的代
码最合适。

第三个示例也还可以,但我认为,如果该运算不是成员函数的话,代码会更清晰一些。

  public static double DotProduct(Vector v1, Vector v2)
  {
      return(v1.x * v2.x + v1.y * v2.y);
  }

C# 和 C++ 重载
与 C++ 相比较,C# 允许重载的运算符很少。有两条限制。首先,成员访问、成员调用(也就是函数调用)、赋值以及
“新建”无法重载,因为这些运算是运行时定义的。

其次,诸如“&&”、“||”、“?:”这样的运算符以及诸如“+=”这样的复合赋值运算符无法重载,因为这会使代码变得
异常复杂,得不偿失。

重载的转换
让我们返回到最初的示例:

Complex i = 5;
Complex sum = i + j;

虽然知道了如何重载加法运算符,但我们仍需要想方法使第一个语句发挥作用。这可以通过对转换进行重载来实现。

隐式和显式转换
C# 同时支持隐式和显式转换。隐式转换是那些总是能成功执行的转换,并且其成功的原因通常是目标类型的范围等于或大
于源类型的范围。从 short 到 int 的转换就是一个隐式转换。隐式转换可以作为赋值语句的一部分:

  short svalue = 5;
  long lvalue = svalue;

显式转换是那些可能导致数据丢失或者引发异常的转换。因此,显式转换要求强制进行类型转换:

  long lvalue = 5;
  short svalue = (short) lvalue;

对转换进行重载时,应该决定转换是隐式还是显式的,但是,应该明白隐式转换模型是安全的,而显式转换则是有风险
的。

将整数值 5 转换为复数的转换定义如下所示:

  public static implicit operator Complex(int value)
  {
      return(new Complex(value, 1.0));
  }

这允许进行从 int 到 Complex 的隐式转换。

语言的互操作性
以上是在 C# 中对运算符进行重载的情况。涉及到其它语言时,事情将变得略为复杂。

运算符重载不是 .NET 公共语言子集中的功能之一,这意味着在某些语言中将无法使用重载。因此,提供非重载的替代方
案是非常重要的,以便在其它语言中仍然能执行相同的运算。如果您的类定义了加法运算符,它还应该定义相同的方法,
使用类似 Add 这样的名称进行命名。

时间: 2024-11-03 15:38:17

帮帮我,我负载过重(C#)的相关文章

frame-关于opencv程序一运行就出错,帮帮我啊

问题描述 关于opencv程序一运行就出错,帮帮我啊 #include"highgui.h" int main(int argc,char** argv) { cvNamedWindow("Example2",CV_WINDOW_AUTOSIZE); CvCapture* capture=cvCreateFileCapture(argv[1]); IplImage* frame; while(1){ frame=cvQueryFrame(capture); if(!

使用 PHP + MySQL 处理负载过重的数据库问题思考和总结

以下是我对使用 PHP + MySQL 处理负载过重的数据库问题思考和总结,经验方面一定有着不少欠缺的地方,欢迎高手前来指教: 在设计时考虑合理的表结构和相应大概要承受多少访问压力,需不需要分表,如果需要分表每个表放多少条记录合适: 对于分表的实现可以采用 PHP 处理或是 MySQL 自带的 MERGE 表类型(即 MRG_MyISAM)来处理: ⑴如果是使用 PHP 处理有两种情况: ①没有数据的新库新表要为抗压做准备,比如一个聊天室一开始就要考虑可能有多个子聊天室,可以根据一些条件来进行,

如何取消58帮帮订阅服务?

1.在网页上关闭订阅服务. 登录了58同城网页,在"浏览记录"里勾选"不再提示",即可取消订阅服务; 2.在58帮帮客户端上关闭订阅服务. 在58帮帮客户端主面板点击"订阅"按钮,打开订阅设置界面,选择"不接受订阅",然后保存,即可完成设置.不再接受订阅信息.

如何使用58帮帮订阅功能?

58帮帮可以帮您订阅58同城的筛选信息. 1.系统自动订阅: 只要您登录以后使用58同城搜索信息,当您的筛选大于等于3个条件时,我们就为您自动保存这个信息为"订阅条件".每天您第一次登陆58同城时,我们都会自动帮您检索您的"订阅条件"是否有更新的内容,如果有更新,我们会在网页上方为您弹出提示; 如果您使用58帮帮客户端,那么客户端会每隔30分钟,为您检索一次您的"订阅条件",如果您的"订阅条件"有更新的内容,58帮帮客户端将弹

58帮帮客户端与网页版的区别?

帮帮分为客户端和网页两种形式. 网页版主要是为了满足普通访客的即时沟通需求,而客户端是为了广大信息发布者,以及商户而生的.帮帮客户端安装登陆后,会驻留在电脑桌面右下方的托盘中,您不需要打开58网页,也能保持时时在线,如果收到客户的问询消息,帮帮客户端能第一时间通知您. 另外,我们还为帮帮客户端在线的用户提供了更增值的服务.在列表页,我们提供了"只看在线"功能,帮助用户筛选在线商家的信息.如果您帮帮客户端在线(网页在线时无效),那么您的信息将被筛选出来,从而获得更多展示机会. 所以,如果

什么是58帮帮?

58帮帮是58同城为广大用户提供的即时通讯工具.通过帮帮,客户和商户可以直接发消息沟通.帮帮分为客户端和网页两种形式. 网页版主要是为了满足普通访客的即时沟通需求,用户无需登录.下载,即可直接在网页上进行会话.而帮帮客户端是为了广大信息发布者,以及商户而生的.客户端登陆后,无需打开58同城网页,也能保持在线状态.帮帮客户端在线的商户,另外客户端还提供了保存聊天记录.好友分组,一键直达用户中心等功能.

如何管理58帮帮多点登录功能?

用户可以在58帮帮桌面客户端上对多点登录功能进行管理. 首先,用户可以选择允许58帮帮桌面版和手机版同时在线,设置方法如下: 第一步:在主面板上打开[系统设置],在[安全设置]中找到[状态管理]界面; 第二步:勾选[允许桌面和手机版客户端同时在线]前的复选框(系统默认设置为已经勾选),然后点击[确定]或[应用]完成设置; 完成设置后,您就可以同时在PC和手机上同时使用58帮帮收发消息啦! 其次,如果由于用户手机丢失或其它特殊原因造成,用户帮帮账号在手机端登录非用户本人操作时,用户可以点击[强制手

如何安装58帮帮安卓手机版客户端?

第一步:下载58帮帮安卓手机版客户端安装包到本地电脑.点击下载 第二步:安装58帮帮安卓手机版客户端. 方法一: 1. 用户通过数据线把手机与电脑相连. 2. 把下载到本地的帮帮安卓手机版客户端通过数据线传输到您手机的SD存储卡上,然后在手机上找到该安装程序,单击即可进入安装流程. 方法二: 1. 用户通过数据线把手机与电脑相连. 2. 如果用户之前在电脑上已经安装例如"360手机助手"或"豌豆荚"等安卓手机管理软件,可以直接通过这类软件安装58帮帮手机版客户端,如

如何卸载58帮帮客户端程序?

Windows系统中卸载"58帮帮"客户端程序指引 第一步:找到控制面板 点击Windows"开始"菜单-选择"控制面板" 第二步:找到58帮帮 ① 在弹出的"控制面板"界面中选择"程序和功能" ② 点击进入"程序和功能"界面后找到"58帮帮"程序 第三步:卸载58帮帮 单击鼠标左键,点选"58帮帮",然后选择控制栏中的"卸载"