C#高级编程6 读书笔记(6)
6.4 运算符重载
运算符重载的关键字是在类实例上不能总是调用方法或属性。
如果要在自己的类上使用运算符,就必须告诉编译器相关的运算符在这个类中的含义。
6.4.1 运算符的工作方式
6.4.2 运算符重载的示例: Vector 结构。
Vector结构表示一个三维矢量。三维矢量只是三个数字的一个集合。 实例见P143。
C#要求所有的运算符重载都要声明为public和static 这表示它们与它们的类或结构相关联。所以运算符重载的代码体不能访问静态成员,也不能访问标识符。
6.5 用户定义的数据类型转换
隐式转换 显式转换
显示转换要在代码中显示标记转换,其方法是在圆括号中写出目标数据类型。
int I=3;
short s=(short)I;
当数据类型转换可能失败或丢失某些数据时。需要显式转换。
1> 把int转换为short时,因为short可能不够大。不能包含转换数值。
2> 把有符号的数据转换为无符号数据,如果有符号的变量包含一个负值,会得到不正确的结果。
3> 把浮点数转换为整数数据类型时,数字的小数部分会丢失。
4> 把可空类型转换为非空类型,null值会导致异常。
如果源数据值会使数据类型转换失败。或者可以会抛出异常,就应把定制数据类型转换定义为显示转换。
6.5.1 执行用户定义的类型转换
C#提供了decimal类,要进行比较复杂的财务处理。仍可以编写自己的结构和类来表示钱款。在主要的类上执行特定的方法。
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace _6._5._1
{
class Program
{
struct Currency
{
public uint Dollars;
public ushort Cents;
Currency balance = new Currency(10, 50);
float f = balance;
public Currency(uint dollars, ushort cents)
{
this.Dollars = dollars;
this.Cents = cents;
}
public override string ToString()
{
return string.Format("(${0}.{1,-2:00}",Dollarts,Cents);
}
public static implicit operator float(Currency value)
{
return value.Dollars + (value.Cents / 100.0f);
}
public static explicit operator Currency(float value)
{
uint dollars = (uint)value;
ushort cents=(ushort)((value-dollars)*100);
return new Currency(dollars,cents);
}
}
static void Main(string[] args)
{
try
{
Currency balance = new Currency(50, 35);
Console.WriteLine(balance);
Console.WriteLine("balance is" + balance);
Console.WriteLine("balance is(using ToString())" + balance.ToString());
float balance2 = balance;
Console.WriteLine("After converting to float,=" + balance2);
balance = (Currency)balance2;
Console.WriteLine("After convering back to Currency,=" + balance);
Console.WriteLine("Now attempt to convert out of range value of" + "-$100.00 to a Currency:");
checked
{
balance = (Currency)(-50.5);
Console.WriteLine("Result is" + balance.ToString());
}
}
catch (Exception e)
{
Console.WriteLine("Exception occurred:" + e.Message);
}
}
}
}
1. 类之间的数据类型转换
定义不同结构或类之间的数据类型转换是允许的但有两个限制。
1> 如果某个类直接或间接继承了另一个类,就不能定义这两个类之间的数据类型转换。
2> 数据类型转换必须在源或目标数据类型的内部定义。
2. 基类和派生类之间的数据类型转换
编译器已近提供了基类和派生类之间的转换。这种转换实际上并没有对象进行任何数据转换。如果要进行的转换时合法的,它们也仅把新引用设置为对对象的引用,这些转换在本质上与用户定义的转换不同。
3. 装箱和拆箱数据类型转换
在使用装箱和拆箱是,这两个过程都把数据复制到新装箱和拆箱对象上。这样对装箱对象的操作就不会影响原来值类型的内容。
6.5.2 多重数据类型转换
如果在进行要求的数据转换时,C#编译器没有可用的直接转换方式,C#编译器就会寻找一种方式,把几种转换会并起来。
第七章 委托和事件
7.1 委托
当要把方法传给其他方法时,需要试用委托。
通常习惯于把数据作为参数传送给方法。
例如:int i=int.parse(“99”);
有时某个方法执行的操作并不是针对数据进行的,而是要对另一个方法进行操作,这就比较复杂了。需要把第二个方法作为参数传递给第一个方法
1> 启动线程—在C#中,可以告诉计算机并运行某些新的执行序列。这种序列就成为线程。
2> 通用库类—有许多库包含执行各种标准任务的代码。
3> 事件—一段时通知代码发生了什么事情。
.NET Framework 不允许把任务函数传递给需要函数指针的方法。如果要传递方法,就必须把方法的细节封装在一种新类型的对象中。即委托。委托指示一种特殊的对象类型,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的指示方法的地址。
7.1.1在C#中声明委托
在C#使用一个类时,分两个阶段。手下需要定义这个类。即告诉编译器这个类由什么字段和方法组成。然后实例化类的一个对象。
使用委托时也需要经过这两个步骤。首先定义使用的委托,对于委托,定义它就是告诉编译器这种类型的委托代表了哪种类型的方法,然后穿件委托的一个或多个实例,编译器在后台将创建表示该委托的一个类。
定义委托的语法如下。
Delegate void IntMehodInvoer(int x);
理解委托的一个重点是它们的类型安全性非常高。
在定义委托时,必须给出它所代表的方法2和返回类型的全部细节。
理解委托的一种好方式是把委托当做给方法签名和返回类型指定名称。
其语法类似于方法的定义,但没有方法体。
因为定义委托基本上是定义一个新类,所以可以在定义类的任何地方定义委托。根据定义的可见性在委托定义上添加一般的访问修饰符:public、private、protected。
定义好委托后就可以创建它的一个实例,以储存特定方法的细节。
7.1.2在C#中使用委托
为了减少输入量,只需要委托的实例,就可以只传输地址的名称这种称为委托推断。
委托推断可以在需要委托实例的任何地方使用。委托推断也可以用于时间,因为事件基于委托。
7.1.3 简单的委托示例
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace _7._1._3
{
class Program
{
class MathsOperations
{
public static double MultiplyByTwo(double value)
{
return value * 2;
}
public static double Square(double value)
{
return value * value;
}
}
delegate double Doubleop(double x);
class Program1
{
static void Main(string[] args)
{
Doubleop[] operations = { MathsOperations.MultiplyByTwo, MathsOperations.Square };
for (int i = 0; i < operations.Length; i++)
{
Console.WriteLine("Using operations[{0}]", i);
ProcessAndDisplayNumber(operations[i], 2.0);
ProcessAndDisplayNumber(operations[i], 7.94);
ProcessAndDisplayNumber(operations[i], 1.414);
Console.WriteLine();
}
}
static void ProcessAndDisplayNumber(Doubleop action, double value)
{
double reault = action(value);
Console.WriteLine("Value is{0},result of operation is {1}", value, reault);
}
}
}
}
7.1.4 BubbleSorter 示例 略…
7.1.5多播委托
前面使用的每个委托都只包含一个方法调用,调用委托的次数与调用方法次数相同。如果要调用多个方法就需要多次显示调用这个委托。委托也可以包含多个方法,这种委托称为多播委托。如果使用多播委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void。否则,只能得到委托调用的最后一个方法的结果。
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace _7._1._5
{
class Program
{
delegate void DoubleOp(double value);
class MathOperations
{
public static void MultiplayByTwo(double value)
{
double result = value * 2;
Console.WriteLine("Mutiplaying by 2:{0} gives {1}", value, result);
}
public static void Square(double value)
{
double result = value * value;
Console.WriteLine("Squaring:{0} gives {1}", value, result);
}
static void ProcrssAndDisplayNumber(DoubleOp action, double valueToProcess)
{
Console.WriteLine();
Console.WriteLine("ProcessAndDisplayNumber called with value={0}", valueToProcess);
action(valueToProcess);
}
}
static void Main(string[] args)
{
DoubleOp operations = MathOperations.MultiplayByTwo;
operations += MathOperations.Square;
ProcessAndDisPlayNumber(operations, 2.0);
ProcessAndDisPlayNumber(operations, 7.94);
ProcessAndDisPlayNumber(operations, 1.414);
Console.WriteLine();
}
}
}
7.1.6 匿名方法
到目前为止,想要使委托工作,方法必须已经存在。但使用委托还有另一种方法:即通过匿名方法。匿名方法是用作委托参数的一个代码块。
匿名方法的有点,减少要编写的代码,不必定义仅由委托使用方法。在为时间定义委托时,有助于降低代码的复杂性。尤其是定义好几个事件时。
但使用匿名方法时,代码执行得不太快。编译器仍定义了一个方法,该方法只有一个自动指定的名称我们不需要知道这个名称。
规则:在匿名方法中不能使用跳转语句到该匿名方法的外部或内部。
在匿名方法内部不能访问不安全代码。
如果需要用匿名方法多次编写同一个功能,就不要使用匿名方法。而编写一个指定方法较好。因为方法编写一次可以多次调用。
<end>


浙公网安备 33010602011771号