C# – class, filed, property, readonly, get, set, init, required 使用基礎

前言

心血來潮,這篇講點基礎的東西。

 

Field

比起 Property,Field 很不起眼,你若問 JavaScript,它甚至都沒有 Field。

但在 C#,class 裏頭真正裝 value 的其實是 Field,Property 只是 Filed 的讀寫器而已。

Field 長這樣

public class Person
{
  public int Age;
}

使用

var person = new Person();
Console.WriteLine(person.Age); // int default is 0 
person.Age = 10; // set value
Console.WriteLine(person.Age); // get value: 10

readonly and init value

如果一個 Field 只能讀,不能寫,可以使用 readonly keyword 去聲明它。

public class Person
{
  public readonly int Age;
}

var person = new Person();
person.Age = 10; // Error : A readonly field cannot be assigned

我們可以用 2 種方式 set init value

public class Person
{
  public readonly int Age = 1;
  public Person()
  {
    Age = 2;
  }
}

一個是在 Field 的結尾寫等於,另一個是通過 construtor。如果兩個都 set,construtor 會 override 掉結尾等於。

注:下面這個寫法是不 ok 的,它無法突破 readonly 的限制哦。

var person = new Person
{
  Age = 15 // Error: A readonly field cannot be assigned
};

Field 的日常(Dependancy Injection)

平時很少會使用 Field 的,絕大部分情況我們用 Property,除了 Dependancy Injection。

public class HomeModel : PageModel
{
  private readonly ILogger _logger;

  public HomeModel(ILogger<HomeModel> logger)
  {
    _logger = logger;
  }

  public void OnGet()
  {
    _logger.LogInformation("Hello World");
  }
}

一個 readonly Field,通過 construtor set init value,然後使用。

C# 12.0 primary construtor 的寫法

public class HomeModel(ILogger<HomeModel> logger) : PageModel
{
  public void OnGet()
  {
    logger.LogInformation("Hello World");
  }
}

primary construtor 的 parameter 會被 compile 成 private Field,所以上面的代碼和上一 part 的代碼基本上是一樣的。

唯一的不同是 primary construtor 目前無法聲明 readonly Field。所以如果我們要 readonly 那就得多加一行。

public class HomeModel(ILogger<HomeModel> logger) : PageModel
{
  private readonly ILogger _logger = logger;
  public void OnGet()
  {
    _logger.LogInformation("Hello World");
    logger.LogInformation("Hello World"); // 注意:logger 依然是可用的,因爲它就是一個 Field 啊
  }
}

 

Property

Property 是 Filed 的讀寫器,它長這樣。

public class Person()
{
  private int _age;
  public int Age
  {
    get { return _age; }
    set { _age = value; }
  }
}

Filed 負責保存 value,Property 負責攔截讀寫過程。

這個是完整的寫法,但日常生活中,大部分情況我們會用語法糖。

Auto Property

public class Person()
{
  public int Age { get; set; }
}

Auto Property 是一種語法糖寫法,前面是 Field,後面配上一個 { get; set; } 表達。它 compile 後長這樣。

compiler 會替我們拆開它們。最終任然是一個 Field、一個 get 方法、一個 set 方法。

readonly = no set

public class Person()
{
  public int Age { get; }
}

把 set 去掉就相等於 readonly Filed。注:沒有 readonly Property 的,只有 no set。

set init value 的規則和 Field 一樣。

public class Person
{
  public int Age { get; } = 1;
  public Person()
  {
    Age = 2;
  }
}

var person = new Person
{
  Age = 3 // Error: Property or indexer 'Person.Age' cannot be assigned to -- it is read only
}

init keyword

上面例子中,實例化 Person 時,賦值是會報錯的。這個是 Field 的規則,我們只可以通過 construtor parameter 去實現 init 賦值。

但這種寫法很多餘,於是 C# 6.0 多了一個 init keyword。

public int Age { get; init; } = 1;

var person = new Person
{
  Age = 3 // no more error
};

把 set 換成 init,它相等於 readonly 但是又允許實例化時賦值。

required keyword

public class Person
{
  public string Name { get; } // Warning Non-nullable property 'Name' must contain a non-null value
}

這是一個 readonly Property,它有一個 warning,因爲我沒有聲明 init value。

public class Person
{
  public string Name { get; } = "init value";
  public Person()
  {
    Name = "init value";
  }
}

我可以通過 2 種方式去設置 init value。這樣就不會有 warning 了。

但是...如果它時 init keyword 呢?

public class Person
{
  public string Name { get; init; } // Warning: Non-nullable property 'Name' must contain a non-null value
}

init 允許我們在實例化時纔給予 init value,也就是說 class 內是可以不需要聲明 init value 的。

但這導致它又 Warning 了。爲了解決這個問題,C# 11 推出了 required keyword。

public class Person
{
  public required string Name { get; init; }
}

聲明 required keyword 後,它就不會 Warning 了。與此同時

var person = new Person(); // Error: Required member 'Person.Name' must be set

如果在實例化時忘記給予 init value,它還會報錯提醒我們哦。

泛型限制也會報錯哦

public class PersonOptions
{
  public required string Name { get; set; }
}

public class Person<T> where T : new() // 泛型限制 T 必須允許無參數實例化
{
}

var person = new Person<PersonOptions>(); // Error : 'PersonOptions' cannot satisfy the 'new()' constraint

 

總結

這篇介紹了 class, filed, property, readonly, get, set, init, required 的基本使用方式。

它之所以有點混亂,主要是因爲 C# 是經過了許多版本才一點一點逐步推出這些特性的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章