Home [C# 筆記] 一切的祖宗object類
Post
Cancel

[C# 筆記] 一切的祖宗object類

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(堆積/堆內存)
  • 拆箱數據 copyheap(堆積/堆內存)數據,放在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

This post is licensed under CC BY 4.0 by the author.