声明类型的基类
c#中的类都继承自object类,包括静态类。
静态类只是无法手动指定继承类型,而声明静态类时的关键字class,已经让他派生自object类了。
结构类型继承自ValueType类,而ValueType继承自object类。
数组继承自Array类,Ayyar类继承自object类。
c#中几乎所有类型都继承自object类。
大多数没有继承object类型的类都是作为变量时声明的类型,
例如接口,例如泛型占位符。
这些类型都是使用特殊手段翻定义才翻出来他们的根本没有到object的继承关系。
这种东西不重要,只是编译阶段的类型,不是运行阶段的类型。他们是可以赋值给object类型的变量的。
而无法赋值给object的值,目前我只找到指针类型。
object的方法
既然几乎所有类型都继承自object,那么我们应该了解一下object的方法。
ToString
这个方法决定了一个类如何转为字符串。
这是一个虚方法,你可以自己决定如何实现它。
他的默认实现是输出GetType的ToString。也就是直接输出类型的完全限定名。
class Fight
{
public int Hp;
public int Atk;
public int Def;
public override string ToString()
{
return $"HP:{Hp},Atk:{Atk},,Def:{Def}";
}
}
Equals
Equals是一个相等判断的方法,用于判断目标值和自己是否一样。
这个方法和==有什么区别,区别是他们就是两个东西。
你可以重写并利用Equals方法,也可以重载并使用==进行判断。
在一般情况下,只会编写其中一种方法,而另一种方法会直接调用编写好的方法。
在模式匹配和查询表达式中,使用is和equals关键字。
这个的意义是告诉你,你重写的东西没用。我要把他以object的Equals进行判断。
另外,在ValueType类型中,Equals方法被重写了。
他会先判断自己和目标类型是否一样,然后使用反射判断所有的字段是否一样。
反射是非常消耗性能的,所以在声明结构的时候,建议重写Equals方法。
class Fight
{
public int Hp;
public int Atk;
public int Def;
public override bool Equals(object obj)
{
return obj is Fight other
&& Hp == other.Hp
&& Atk == other.Atk
&& Def == other.Def;
}
}
GetHashCode
这个方法决定了一个类如果获取他的哈希值。也是一个虚方法。
哈希值是一个普通的int类型。任何类都可以转为string,那要求任何类都可以转为int也很合理吧。
哈希值算是一种特征码。一个类有不同值的时候,结果尽量不同。
并且如果一个类在Equals判断为相等时,要求得到同样的哈希值。
并且尽量均匀覆盖所有的int。
比如记录人的生日取哈希值的时候,如果取年份那就补不均匀了。
活着的人全挤在1900-2100之间。并且1980-2010之间的数字特别多。
如果取月份,那就有点均匀了。但月也有大月和小月的区分,特别是2月最少。
所以,这个方法对怎样取值没用硬性规定。你应该结合你的类的字段,自己做出判断。
GetType
GetType不是虚方法,不能重写。
这个方法用于获得在反射时使用的,类的类型。
反射大致是一种让已经编译好的程序,回过头来看源码的功能。
但只能看到类成员的声明和定义。
而GetType就是看到这个类型的定义。
这个方法不能重写所以不会骗人。
例如有一种声明类型的方法是这样的
using System.Security.Cryptography;
MD5 md5= MD5.Create();
他不使用传统的new MD5()构造器的方式。
如果使用构造器那一定会得到MD5类型。
但使用静态方法就不一定了。他可能先调用了new MD5,
然后改了一些值。如果无法成功就给你一个null。
又或者他挑了一个继承自MD5的派生类,用多态的方式假装给你一个MD5。
Console.WriteLine(md5.GetType().Name);
用GetType可以看到他真正的类型,果然不是MD5。
MemberwiseClone
这个方法是一个protected方法,不能被外部调用。他也不能被重写,只是给你用的。
他的效果是获取当前类的浅拷贝,然后把这个复制的新对象给你。
不过由于是在object里声明的,而且不能重写,所以返回类型是object。
但放心,返回的值就是你这个类的类型,安心强转他。
class Tesk
{
public Tesk Clone()
{
return (Tesk)MemberwiseClone();
}
}
所谓浅拷贝,是指把所有字段全都复制一遍。
而我们说过,引用类型只是复制的话会窜号。
引用类型保存的是他的地址,或者说快捷方式。两份快捷方式打开的是同一份内容。
而深拷贝就是在遇到引用类型时递归这个过程,把引用类型也浅拷贝,如果里面还有引用类型继续浅拷贝。
这样一来,引用类型的内容和原来的内容一样,但不会窜号了。
但这个过程太过复杂,也只能用反射操作性能也低。
甚至要考虑出现循环引用的时候会不会出现bug。所以没给深拷贝的方法。
另外,值类型在赋值的时候,可能就是在自动调用这个方法。