一、两类的比较,在介绍之前先来认识封箱和拆箱:
1.封箱(boxing)是把值类型转换为System.Object 类型,或者转换为由值类型实现的接口类型。
拆箱(unboxing)是相反的转换过程。
struct MyStruct
{
public int Val;
}
MyStruct valType1 = new MyStruct();
valType1.Val = 5;
object refType = valType1;
以这种方式封箱变量而创建的对象,包含值类型变量的一个副本的引用,而不包含源值类型变量的引用。
valType1.Val = 6;
MyStruct valType2 = (MyStruct)refType;
Console.WriteLine("valType2.Val = {0}", valType2.Val);
执行这段代码将得到如下输出结果:
valType2.Val = 5
但在把一个引用类型赋予对象时,将执行不同的操作。把MyStruct 改为一个类(不考虑这个类名不合适的情况),即可看到这种情形:
class MyStruct
{
public int Val;
}
如果不修改上面的客户代码,就会得到如下输出结果:
valType2.Val = 6
也可以把值类型封箱到一个接口类型中,只要它们实现这个接口即可。例如,假定MyStruct 类型实现IMyInterface 接口,如下所示:
interface IMyInterface
{
}
struct MyStruct : IMyInterface
{
public int Val;
}
MyStruct valType1 = new MyStruct();
IMyInterface refType = valType1;
//拆箱
MyStruct ValType2 = (MyStruct)refType;
封箱是在没有用户干涉的情况下进行的(即不需要编写任何代码),但拆箱一个值需要进行显式转换,即需要进行数据类型转换(封箱是隐式的,所以不需要进行数据类型转换)。
封箱非常有用,有两个非常重要的原因。首先,它允许在项的类型是object 的集合(如ArrayList)中使用值类型。其次,有一个内部机制允许在值类型上调用object,例如int 和结构。
在比较对象时,常常需要了解它们的类型,才能确定是否可以进行值的比较,之前介绍了GetType()方法,所有的类都从System.Object 中继承了这个方法,这个方法和typeof()运算符一起使用,就可以确定对象的类型(并据此执行操作):
if (myObj.GetType() == typeof(MyComplexClass))
{
// myObj is an instance of the class MyComplexClass.
}
2.本节将介绍比较值的一种简便方式:is 运算符。它可以提供可读性较高的代码,还可以检查基类。
is 运算符并不是说明对象是某种类型的一种方式,而是可以检查对象是否是给定类型,或者是否可以转换为给定类型,如果是,这个运算符就返回true。
is 运算符的语法如下:
<operand> is <type>
这个表达式的结果如下:
如果<type>是一个类类型,而<operand>也是该类型,或者它继承了该类型,或者它可以封箱到该类型中,则结果为true。
如果<type>是一个接口类型,而<operand>也是该类型,或者它是实现该接口的类型,则结果为true。
如果<type>是一个值类型,而<operand>也是该类型,或者它可以拆箱到该类型中,则结果为true。
二、值比较:
进行值比较,可以使用运算符重载。另一个方法是使用IComparable 和IComparer 接口,它们可以用标准的方式定义比较对象的过程。这是由.NET Framework 中各种集合类提供的方式,是对集合中的对象进行排序的一种绝佳方式。
1)运算符重载:
说明:
bool op3 = op1 == op2;
使用==二元运算符来比较两个对象,看看它们是否引用同一个对象,而不是验证它们的值是否相等。
我们知道对象直接不能直接相加减,这时候就需借助运算符重载。
要重载运算符,可给类添加运算符类型成员(它们必须是static)。一些运算符有多种用途(如-运算符就有一元和二元两种功能),因此我们还指定了要处理多少个操作数, 以及这些操作数的类型。一般情况下,操作数的类型与定义运算符的类相同,但也可以定义处理混合类型的运算符
public class AddClass1
{
public int val;
public static AddClass1 operator +(AddClass1 op1, AddClass1 op2)
{
AddClass1 returnVal = new AddClass1();
returnVal.val = op1.val + op2.val;
return returnVal;
}
}
这时候就可以使用
AddClass1 op3 = op1 + op2;
可以重载下述运算符:
一元运算符:+, -, !, ~, ++, --, true, false
二元运算符:+, -, *, /, %, &, |, ˆ, <<, >>
比较运算符:==, !=, <, >, <=, >=
如果重载true 和false 运算符,就可以在布尔表达式中使用类,例如,if(op1){}
不能重载赋值运算符,例如+=
2)IComparable和IComparer接口:
IComparable 和IComparer 接口是.NET Framework 中比较对象的标准方式。这两个接口之间的差别如下:
IComparable 在要比较的对象的类中实现,可以比较该对象和另一个对象。
IComparer 在一个单独的类中实现,可以比较任意两个对象。
一般使用IComparable 给出类的默认比较代码,使用其他类给出非默认的比较代码。
IComparable 提供了一个方法CompareTo(),这个方法接受一个对象。例如,实现可以为实现方法传送一个Person 对象,以便确定这个人是否比当前的人更年老还是更年轻。实际上,这个方法返回一个int,所以也可以确定第二个人与当前的人的年龄差:
if (person1.CompareTo(person2) == 0)
{
Console.WriteLine("Same age");
}
else if (person1.CompareTo(person2) > 0)
{
Console.WriteLine("person 1 is Older");
}
else
{
Console.WriteLine("person1 is Younger");
}
IComparer 也提供了一个方法Compare()。这个方法接受两个对象,返回一个整型结果,这与CompareTo()相同。对于支持IComparer 的对象,可以使用下面的代码:
if (personComparer.Compare(person1, person2) == 0)
{
Console.WriteLine("Same age");
}
.NET Framework 在类Comparer 上提供了IComparer 接口的默认实现方式,类Comparer 位于System.Collections 名称空间中,可以对简单类型以及支持IComparable 接口的任意类型进行特定文化的比较。
int firstNumber = 35;
int secondNumber = 23;
Console.WriteLine("Comparing ‘{0}’ and ‘{1}’, result: {2}",
firstNumber, secondNumber,
Comparer.Default.Compare(firstNumber, secondNumber));
拓展:
可以使用as 运算符把一个值转换为引用类型。如果不能进行转换,as 运算符就返回null 值。
as 运算符使用下面的语法,把一种类型转换为指定的引用类型:
<operand> as <type>
这只适用于下列情况:
<operand>的类型是<type>类型
<operand>可以隐式转换为<type>类型
<operand>可以封箱到<type>类型中
如果不能从<operand>转换为<type>,则表达式的结果就是null。