Object 類型
- 對於 C# 中所有class,默認的父類或是最終基都是
Object類(System命名空間下,簡寫為object)
1
2
3
4
5
6
System.Object
---------------
string ToString();
System.Type GetType();
int GetHashCode();
bool Equals();
string ToString()
- 用於”輸出”當前物件信息,即將當前物件的字段們都轉化為字串,以一定的格式輸出
- 默認情況:輸出當前物件的類型全名(namespace + class)
- 用途:有時候我們有特定的輸出需求,則可以重寫
ToString
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString() {
return $"Id= {Id}, Name={Name}";
}
}
internal class Program
{
static void Main(string[] args) {
Student s = new Student();
s.Id = 123;
s.Name = "Rii";
Console.WriteLine(s.ToString());
Console.Read();
}
}
//未重寫ToString輸出:Demo.Student
//重寫ToString輸出:Id= 123, Name=Rii
System.Type GetType()
- 用於獲取當前物件的類型信息(反射),
Type:FullName:完整類型名,命名空間+類別Name:類別IsValueType:是否為「值類型」IsClass:是否為「引用類型」
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student {
public int Id { get; set; }
public string Name { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
object o = new Student();
Type t = o.GetType();//GetType返回Type類型
Console.WriteLine(t.FullName); //輸出:Demo.Student
Console.WriteLine(t.Name);//輸出:Student
Console.WriteLine(t.IsValueType);//輸出:False
Console.WriteLine(t.IsClass);//輸出:True
Console.Read();
}
}
bool Equals()
- 用於判斷兩個物件是否相等,與
==一致 ==:「值類型」的值相同,「引用類型」的地址相同,字串的字符相同- 需求:有時候我們有特定的相等判定需求,比如兩個物件id相同才算相等、各個成員變量相同才算相等
- 方法:對
Equals方法進行重寫
範例:物件指向不同內存地址
Equals結果輸出 false兩者並不相等,因為他們指向不同內存地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student {
public int Id { get; set; }
public string Name { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
Student s1 = new Student();
s1.Id = 123;
s1.Name = "Rii";
Student s2 = new Student(); //不同內存地址
s2.Id = 123;
s2.Name = "Rii";
Console.WriteLine(s1.Equals(s2)); //輸出:false
Console.Read();
}
}
範例:物件指向相同內存地址
如果改成s2=s1,它們指向相同內存地址,Equals結果輸出就會 true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student {
public int Id { get; set; }
public string Name { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
Student s1 = new Student();
s1.Id = 123;
s1.Name = "Rii";
Student s2 = s1; //相同內存地址
Console.WriteLine(s1.Equals(s2)); //輸出:true
Console.Read();
}
}
範例:重寫 Equals 方法
如果我們希望使用 Equals方法,只要它們的內容是相同的,就返回 true,那就要重寫 Equals()方法了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Student {
public int Id { get; set; }
public string Name { get; set; }
public override string ToString() {
return $"Id= {Id}, Name={Name}";
}
public override bool Equals(object obj)
{
//1.嘗試將obj轉換為Student類型物件
Student o = obj as Student; //轉換成功返回對應的對象(物件),轉換失敗返回null
//2.如果轉換不成功,二者不具備可比對性
if (o == null) return false;
//3.如果轉換成功,則二者屬性一一對比
return Id == o.Id && Name == o.Name;
}
}
internal class Program
{
static void Main(string[] args)
{
Student s1 = new Student();
s1.Id = 123;
s1.Name = "Rii";
Student s2 = new Student();
s2.Id = 123;
s2.Name = "Rii";
Console.WriteLine(s1.Equals(s2)); //重寫Equals方法後,內容相同就會返回true
Console.Read();
}
}
裝箱與拆箱
- 裝箱:將「值類型」轉換為「
Object類型」的過程 - 拆箱:從
Object中提取「值類型」的過程
1
2
3
4
5
6
//裝箱
int i = 123;
object o = i;
//拆箱
int j = (int)o;
案例:請實現將各類數據裝入同一個數組的過程,{1,3.4,”abc”}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void Main(string[] args)
{
//裝箱&拆箱
int i = 123;
object o = i; //裝箱:將數字i放入o物件中
int j = (int)o; //拆箱:將數字123從o物件中拿出來,給到變數j
//請編寫一個數組 { 1, 2.5, "abc", cat }
//1. 裝箱&拆箱
//2. object 作為基類
Cat cat = new Cat();
//對於1跟2.5這種值類型,都進行了裝箱操作
//"abc"是引用類型,string類型的對象,string的祖宗是 object
//cat 是引用類型,是Cat類的對象,因為cat的父類/祖宗是 object
//所以前兩個是裝箱的過程,後兩個是子類轉換為父類對象的過程
object[] objs = { 1, 2.5, "abc", cat };
}
- 1跟2.5是「值類型」,都進行了裝箱操作
- “abc”是「引用類型」,將 string 類型的對象放到 objs 中,是一個繼承的關係,因為string 的祖宗是 object,所以是可以直接作為一個 object 類型的對象放到裡面
- cat 是「引用類型」,是Cat類的對象,因為cat的父類/祖宗是 object,也可以轉換為一個object 類型的對象
- 所以前兩個是裝箱的過程,後兩個是子類轉換為父類對象的過程
裝箱與拆拆箱內存原理
int i =123;是放到stack(堆疊/棧內存)中,隨後 object o因為o是 object類型,是一個物件,那它一定是一個「引用類型」,按照「引用類型」我們需要new 一個object 把它放到 heap(堆積/堆內存)裡面,但是它現在直接等於了i,所以呢我們中間 new 這些之類的步驟就被省略了,它是自動執行的,相當於在heap(堆積/堆內存)直接搞出來了一塊內存,而這塊內存呢就是一個 object類型的內存,只不過我們是把 123 這個的數字放到了這塊內存當中,僅此而己,那其實呢,它會被包裝成為一個 object 類型的物件,隨後呢,這塊內存一定有它的地址,咱們再把地址給到 object 類型的物件 o,所以說,這個物件其實呢就被分配到了heap(堆積/堆內存)上,並且裡面還蘊含著一個值叫做123。
隨後int j=(int)o;等於做了一個拆箱的動作,轉換成一個int類型的數字,其實它的過程就是這樣,他們會把裡面所蘊含的這個123的數字,copy一份到 j 當中,所以說,你看這是一個雙向copy 的過程。
在裝箱的時候,把123 拷貝到了咱們 object所在的heap(堆積/堆內存)裡面,
然後在拆箱的時候,將 object 裡面的這個123 這個數值,再拷貝出來給到了這個 j。
總結
- 原數據依舊儲存在
stack(堆疊/棧內存) - 裝箱數據 copy原數據,放在
heap(堆積/堆內存) - 拆箱數據 copy
heap(堆積/堆內存)數據,放在stack(堆疊/棧內存)
裝箱與拆箱作用
增大程式靈活性,考慮性能的時候使用「值類型」,特殊需要的時候採用裝箱「引用類型」
(轉換是需要時間成本的)給予了數據統一的類型,可以統一進行處理
(3,1.2,’c’,1.5f 轉換成object會被自動裝箱成為一個object 類型的物件)
1
2
3
4
5
6
7
8
9
10
static void Func(object o) {
Type t = o.GetType();
if (t == typeof(int)) {
Console.WriteLine("int");
} else if (t == typeof(float)) {
Console.WriteLine("float");
} else if (t == typeof(string)) {
Console.WriteLine("string");
}
}
https://www.bilibili.com/video/BV1Ea4y1g7Rh?t=7.7
