共计 2908 个字符,预计需要花费 8 分钟才能阅读完成。
咱们来看一下这个代码:
DayOfWeek.Friday.GetType()
DayOfWeek.Friday 是一个枚举值,它的这个 GetType()是哪里冒出来的?联合咱们后面所学的常识,子类中找不到的成员,肯定是来自于它的父类,是不是?然而,enum DayOfWeek 又没有继承啊?
欲知详情,有一个简略粗犷的方法:间接在 GetType()上 F12,马上转到办法定义:
哈哈,GetType()就是
Object
的实例办法嘛!
实际上,Object 是 C# 中所有类型的父类,能够说它是万物之祖。因而,任何类默认主动的继承自 Object,不须要显式申明。
Object 还有其余几个罕用的办法:
1、Equals():留神这里有它是一个静态方法,用于比拟作为参数传入的两个对象:
- 如果是值类型,比拟两个对象的值内容(所有字段),雷同为 true,否则为 false
- 如果是援用类型,
- 如果有一个值是 null 值,返回 false,否则
- 当两个变量指向同一个对象,返回 true;否则为 false
struct Bed // 值类型 //class Bed // 援用类型 {internal int Id { get; set;} }
检测代码:
Bed a = new Bed(); a.Id = 1; Bed b = new Bed(); b.Id = 1; // 或者 =2 // 如果 Bed 是 struct,true // 如果 Bed 是 class,false Console.WriteLine(Equals(a, b));
演示:
对于 ==
- 值类型:
内置的简略值类型:比拟值
自定义的值类型:默认不反对,须要本人进行运算符重载
enum:比拟底层数值 - 援用类型:同 Equals(),除非进行了运算符重载
- string:相似于值类型,且两个为 null/empty 的字符串雷同
2、GetHashCode():获取对象的哈希值。哈希(Hash),有被称之为散列,是一种获取固定长度数据的算法。GetHashCode()能够获取将以后对象进行 Hash 估算之后的后果。
留神:GetHashCode()是 virtual 的,所以能够被子类重写(override)。
如果没有被重写,两个完全相同的对象 GetHashCode()的 Hash 值必然雷同。然而,雷同的 Hash 值并不意味着生成他们的对象雷同!所以,咱们能够用 GetHashCode()确定两个对象不雷同,但不能用于确定他们雷同。
// 接下面代码 ///Bed 为值类型,输入相等 ///Bed 为援用类型,输入不同 Console.WriteLine(a.GetHashCode()); Console.WriteLine(b.GetHashCode());
演示:
_3、GetType():获取以后对象的类型信息。留神这里获取的是以后对象的类型信息,不是以后变量的类型信息。什么意思呢?看代码:
_
Person wx = new Student(); Console.WriteLine(wx.GetType().Name); // 后果为:Student
_Person 就是 wx 这个变量的类型,而 Student 就是 wx(指向的)这个对象的类型。又因为 wx 到底指向的是什么对象,只能是在程序运行时能力晓得,所以 GetType()又被称之为获取变量的“运行时类型”。常常与之绝对比的是:typeof(),它获取的是类的类型信息:
_
Console.WriteLine(typeof(Student));
_
因为传入 typeof 的就是一个类,所以能够在编译时就晓得这个类的类型信息,它又被称之为获取类型的“编译时类型”。
这是一个常见的面试题,同学们要留神。
4、ToString():将对象转换成字符串。留神这也是一个 virtual 办法,所以很多罕用的.NET 类库对象都重写了这个办法,比方 DateTime。如果没有重写的话,默认返回对象的类的全名。
_
Console.WriteLine(DateTime.Now.ToString()); Console.WriteLine(new Student().ToString());
好了,终于到了咱们能够零碎的捋一捋.NET 中所有类型关系的时候了,一图胜千言:
咳咳,……,嗯,这个图看起来可能还是有点晕,解释一下:
- 所有类型被分为值类型(Value Type)和援用类型(Reference Type)
- 但都继承自 Object
- 所有 class 定义的都是援用类型(数组继承自 Array,是援用类型;String 是一个行为特色十分相似于值类型的援用类型),其余 struct/enum 定义的都是值类型
所以,在 C# 中,“万物皆对象”,就是一个事实:所有的类都继承自 Object,所以,所有的对象都是 Object 对象——没故障。
那么,这就波及到上面一个问题:
装箱和拆箱
咱们来看这样几行代码:
object wx = new Student(); // 把 new Student()的堆地址存到 wx 变量中 int i = 888; // 把值 888 间接寄存到变量 i 中 object o = 986; // 把 986 存到哪里?
留神最初一行代码,986 能不能间接放到变量 o 外面?这是不行的,因为 o 是 object 援用类型的,o 外面只能寄存一个堆地址。然而 object o =986; 又是合乎语法的,这种把值类型数据间接赋值给 object 变量的行为,被称之为装箱(box)。具体来说,这一行代码做了两件事:
- 主动生成一个 object 对象(相当于 new object())
- 将整数值 986 存入这个 object 对象(这个过程就是装箱的外围过程)
- 将 object 对象的堆地址赋值给 o 变量
写成伪代码就是:
object o = new object(); o.inbox = 986; //inbox 是我顺手写的,代指“箱子外部”
咱们能够用 & 运算符查看:
int i = 986; object o = 986;
既然可能装箱,当然也可能拆箱(unbox)
int i = (int)o;
留神:装箱和类型的强制转换语法完全一致,然而不要将他们一概而论:
- 装箱和拆箱仅实用于值类型和 object 之间;强制类型转换实用于任何有继承关系的类型
- 装箱和拆箱多了一个创立 object 实例的过程;类型转换没有这个过程,不会额定的生成一个对象
最初的最初,咱们来学习 C#4.0 因为因为 Office Automation APIs 和 Iron Python libraries 而引入的一种新类型
动静类型(dynamic)
学习这个类型,齐全是为了面试须要。^_^
很多时候,dynamic 齐全和 object 完全一致,任何类型对象都能够应用 dynamic 标记。应用 dynamic 标记的变量能够绕过 C# 的编译时(留神:仅仅是编译时)的类型查看。
object o = “986”; Console.WriteLine(o – 88); // 编译时谬误 dynamic d = “986”; Console.WriteLine(d.GetType()); // 依然是 string 类型 Console.WriteLine(d – 98); // 绕过了类型查看
然而,dynamic 并不象征这变量类型可变,通过上述 d.GetType()能够分明的看到 d 的类型;而且,当咱们运行 d -98 时会报出因类型不匹配造成的谬误: