C# 修飾符二

修飾符

abstract

  • abstract 修飾符 主要用于定義抽象類和抽象方法
  • abstract 修飾符可用于類、方法、屬性、索引和事件
  • 抽象類本身不能被實例化,通常用于定義具有共同特征的一組類的基類,提供一個可供多個派生類共享的通用基類定義,抽象類可以包含抽象方法和具體方法
  • 標記為 抽象的成員 必須由 派生自抽象類 的 非抽象類 來實現(xiàn)

抽象屬性

  1. 抽象屬性是在抽象類或接口中聲明的屬性,抽象類可以包含抽象屬性和具體屬性,而接口只能包含抽象屬性
  2. 抽象屬性沒有具體的實現(xiàn),只包含屬性的聲明
  3. 抽象屬性必須由派生類提供具體實現(xiàn),派生類使用 override 關鍵字來實現(xiàn)抽象屬性
  4. 派生類可以選擇性地實現(xiàn)抽象屬性
    • 派生類是非抽象類,必須提供對抽象屬性的實現(xiàn)
    • 派生類也是抽象類,選擇性地將抽象屬性留給它的派生類來實現(xiàn)
  5. 抽象屬性不能使用 static 關鍵字修飾,因為抽象屬性需要由具體的實例提供實現(xiàn)
  6. 抽象屬性不能是私有的,不能使用 private、protected 或 internal 等訪問修飾符,因為它們需要在派生類中被訪問和實現(xiàn)
public abstract class MyAbstractClass
{
    public abstract int MyAbstractProperty { get; set; } // 抽象屬性
    public int MyConcreteProperty { get; set; } // 具體屬性
}

public interface IMyInterface
{
    int MyAbstractProperty { get; set; } // 抽象屬性
}

public class MyDerivedClass : MyAbstractClass
{
    private int myProperty;

    public override int MyAbstractProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}

抽象方法

  1. 抽象方法是在抽象類或接口中聲明的方法,抽象類可以包含抽象方法和具體方法,而接口只能包含抽象方法
  2. 由于抽象方法聲明不提供實際的實現(xiàn),因此沒有方法主體;方法聲明僅以分號結尾,且簽名后沒有大括號 ({ })
  3. 抽象方法必須由派生類提供具體實現(xiàn),派生類使用 override 關鍵字來實現(xiàn)抽象方法
  4. 派生類可以選擇性地實現(xiàn)抽象方法
    • 派生類是非抽象類,必須提供對抽象方法的實現(xiàn)
    • 派生類也是抽象類,選擇性地將抽象方法留給它的派生類來實現(xiàn)
  5. 抽象方法不能使用 static 關鍵字修飾,因為抽象方法需要由具體的實例提供實現(xiàn)
  6. 抽象方法不能是私有的,不能使用 private、protected 或 internal 等訪問修飾符,因為它們需要在派生類中被訪問和實現(xiàn)
  7. 派生類可以選擇使用 new 關鍵字隱藏基類的抽象方法,但這并不是實現(xiàn)抽象方法的最佳方式,通常應該使用 override 關鍵字
public abstract class MyAbstractClass
{
   public abstract void MyAbstractMethod(); // 抽象方法,沒有方法體
   public void MyConcreteMethod() // 具體方法
    {
        // 具體實現(xiàn)
    }
}

public interface IMyInterface
{
    void MyAbstractMethod(); // 抽象方法
}

public class MyDerivedClass : MyAbstractClass
{
    // 1. 抽象方法實現(xiàn)override
    public override void MyAbstractMethod()
    {
        // 具體實現(xiàn)
    }
   // 2. 抽象方法實現(xiàn) new
    public new void MyAbstractMethod()
    {
        // 具體實現(xiàn),但并不是最佳實踐
    }
}

抽象類

抽象類具備以下功能:

  1. 抽象類不能實例化
  2. 抽象類可以包含抽象屬性、抽象方法、具體方法、訪問器
  3. 抽象類 中 的具體方法,具體屬性不一定需要在派生類中進行實現(xiàn),可以有抽象類提供默認實現(xiàn),或派生類選擇性覆蓋活繼承
  4. 一個類包含一個或多個抽象方法,該類必須被聲明為抽象類
  5. 當一個類派生自一個抽象類并且該抽象類包含抽象方法或屬性(包括索引器)時,派生類必須提供對這些抽象成員的具體實現(xiàn),因為抽象方法和屬性是沒有實際實現(xiàn)的,它們需要在派生類中被具體實現(xiàn)
  6. 若抽象類實現(xiàn)了某個接口,則必須為接口中的所有成員提供實現(xiàn),因為接口成員默認是抽象的,而抽象類不能包含未實現(xiàn)的抽象成員
  7. 抽象類可以通過將接口方法映射到抽象方法上來提供接口成員的實現(xiàn)
  8. 無法使用 sealed 修飾符來修改抽象類,因為兩個修飾符的含義相反。 sealed 修飾符阻止類被繼承,而 abstract 修飾符要求類被繼承
public interface IShape
{
    double Area { get; }
    int this[int index] { get; set; }
    void Draw();
}

// 抽象類實現(xiàn)接口
public abstract class MyAbstractClass : IShape
{
    private double area;
    private int[] values = new int[3];

    // 抽象屬性
    public abstract string ShapeType { get; }

    // 實現(xiàn)接口屬性
    public double Area => area;
 
   // 抽象索引器
    public abstract int this[int index] { get; set; }
  
  // 實現(xiàn)接口索引器
    public int this[int index]
    {
        get { return values[index]; }
        set { values[index] = value; }
    }

    // 實現(xiàn)接口方法
    public void Draw()
    {
        Console.WriteLine("Drawing the shape in MyAbstractClass.");
    }

    // 具體方法
    public void Display()
    {
        Console.WriteLine("Displaying the shape.");
    }
 
    // 抽象方法,必須由派生類提供具體實現(xiàn)
    public abstract void AbstractMethod();

    // 抽象訪問器
    public abstract string AccessorExample { get; set; }
}

// 派生類
public class MyDerivedClass : MyAbstractClass
{
     // 實現(xiàn)抽象屬性
    public override string ShapeType => "Circle";

    // 提供對抽象成員的具體實現(xiàn)
    public override void AbstractMethod()
    {
        Console.WriteLine("Abstract method implemented in MyDerivedClass.");
    }

    // 實現(xiàn)抽象索引器
    public override int this[int index]
    {
        get { return index * 2; }
        set { /* 實現(xiàn)索引器的 set 方法 */ }
    }

   // 實現(xiàn)抽象訪問器
    public override string AccessorExample
    {
        get { return "AccessorExample value"; }
        set { Console.WriteLine($"AccessorExample set with value: {value}"); }
    }
}

class Program
{
    static void Main()
    {
        MyDerivedClass derivedInstance = new MyDerivedClass();

        // 使用具體方法
        derivedInstance.Display();  // 輸出 "Displaying the shape."

        // 使用實現(xiàn)的接口方法、屬性、索引器
        derivedInstance.Draw();  // 輸出 "Drawing the shape in MyAbstractClass."
        double area = derivedInstance.Area;
        Console.WriteLine($"Area: {area}");

        derivedInstance[0] = 1;
        derivedInstance[1] = 2;
        derivedInstance[2] = 3;
        Console.WriteLine($"Values: {derivedInstance[0]}, {derivedInstance[1]}, {derivedInstance[2]}");

        // 使用派生類提供的具體實現(xiàn)
        derivedInstance.AbstractMethod();  // 輸出 "Abstract method implemented in MyDerivedClass."

        // 使用抽象訪問器
        string accessorValue = derivedInstance.AccessorExample;
        Console.WriteLine($"AccessorExample: {accessorValue}");
        derivedInstance.AccessorExample = "New Value"; // 輸出 "AccessorExample set with value: New Value"

    }
}

問題??

對于abstract,什么情況下繼承base class,什么情況下implement interface,IoC大場景下為什么使用interface

繼承 base class:

  • 定義共享的行為:當有一組類擁有一些共同的行為或屬性時,可以將這些公共的行為和屬性抽象出來,放到抽象類中,因為抽象類通常用于定義具有共同特征的一組類的基類,使這些類能夠繼承這個抽象類并共享這些行為
  • 提供默認實現(xiàn): 抽象類可以包含具體方法,這些方法提供了默認的實現(xiàn),而派生類可以選擇性地覆蓋或者繼承這些實現(xiàn)
  • 共享狀態(tài)或字段: 如果一組相關的類需要共享一些狀態(tài)或字段,抽象類可以在其中定義這些狀態(tài),并提供對這些狀態(tài)的訪問
public abstract class Animal
{
    // 共享字段
    protected int speed;
    // 定義共享的行為
    public abstract void MakeSound();
    // 提供默認實現(xiàn)
    public void HasName()
    {
        Console.WriteLine("Animals have names");
    }

    public void Accelerate()
    {
        speed += 10;
    }

    public void Brake()
    {
        speed -= 10;
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Woof!");
    }
}

public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Meow!");
    }
}

implement interface:

  • 在多繼承的情況下:C# 中一個類只能繼承自一個直接的基類,但可以實現(xiàn)多個接口,如果抽象類需要具備 某些接口定義 的行為,而同時也需要繼承自另一個基類,那么可以通過實現(xiàn)接口來滿足多繼承的需求
  • 強調規(guī)范: 接口提供了一種規(guī)范,用于定義類應該具有的方法、屬性等。通過實現(xiàn)接口,抽象類可以強調其派生類應該實現(xiàn)某些行為,并確保符合特定的接口規(guī)范
  • 提供通用功能: 抽象類可以通過實現(xiàn)接口提供一些通用的功能,這些功能可以在所有派生類中共享。這有助于確保派生類都提供了某些基本的行為,同時允許它們實現(xiàn)各自特定的功能
public interface ILogging
{
    void LogMessage(string message);
}

public abstract class BaseClass
{
    // Common functionality for the base class
}
// 多繼承
public abstract class LoggingBaseClass : BaseClass, ILogging
{
    public void LogMessage(string message)
    {
        // Log message implementation
    }
}

public interface IShape
{
    bool Authenticate();
    double CalculateArea();
}

public abstract class ShapeBase : IShape
{
   // 強調規(guī)范 和 提供通用功能
    public abstract double CalculateArea();
    public bool Authenticate()
    {
        // Authentication logic
        return true;
    }
}

IoC場景使用interface:

  • 依賴性:
    • 在IoC中使用 接口,可以讓 類只依賴于接口定義的契約,而不依賴于具體的實現(xiàn)
    • 在IoC中使用 抽象類 ,意味著 派生類 必須實現(xiàn)所有的抽象方法,被迫提供某些默認實現(xiàn)
  • 繼承性:
    • 在IoC中使用 接口,接口允許多個類實現(xiàn)相同的接口,從而實現(xiàn)了多態(tài)性,同時一個類可以實現(xiàn)多個接口,IoC 容器能夠根據(jù)需要選擇合適的實現(xiàn)進行注入,提供了更大的靈活性
    • 抽象類在繼承上有單一繼承的限制,一個類只能繼承自一個抽象類。這在一些情況下可能導致設計上的約束,特別是當需要在一個類中使用多個不同的功能或模塊時
  • 測試性:
    • 通過接口,可以更容易地進行單元測試。IoC 容器可以使用模擬對象或者測試實現(xiàn)來替代接口的實際實現(xiàn),從而隔離和測試系統(tǒng)中的不同部分
    • 抽象類可能包含具體的實現(xiàn),這使得在進行單元測試時難以使用模擬對象。在進行單元測試時,最好是依賴于接口而不是具體的實現(xiàn),以便輕松地替換為模擬對象

總的來說,雖然抽象類在某些情況下是有用的,但在 IoC 場景中,由于上述一些限制和缺陷,通常更傾向于使用接口。接口提供了更大的靈活性、松耦合性和可測試性,更適合在 IoC 容器中進行依賴注入,也可以根據(jù)具體需求和設計目標選擇使用接口還是抽象類,有時甚至兩者結合使用

abstract: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/abstract

const

  • 使用 const 關鍵字來聲明某個常量字段或局部變量
  • const 字段只能在該字段的聲明中初始化
  • 常量是一種在程序執(zhí)行期間其值不能被改變的標識符,常量在編譯時被解析,而不是在運行時,意味著常量的值在程序運行前就已經(jīng)確定,并且不能在運行時修改
  • 常量通常用于定義不會改變的值
class Program
{
    // Pi 和 MaxValue 都是常量
    const double Pi = 3.14159;
    
    static void Main()
    {
        const int MaxValue = 100;
        
        Console.WriteLine($"The value of Pi is: {Pi}");
        Console.WriteLine($"The maximum value is: {MaxValue}");
    }
}

const: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/const

event

  • event 關鍵字用于 聲明 發(fā)布服務器類中的事件
  • 事件是一種在 類或對象 中定義的 成員,允許 其他類或對象 訂閱(或取消訂閱)該事件,可以在事件發(fā)生時執(zhí)行特定的操作
  • 事件的聲明通常包含兩部分:事件的類型和事件處理程序的委托類型
  • 事件的類型通常是一個委托類型,用于指定事件處理程序的方法簽名
  • 事件通常在類內的某個地方被觸發(fā),即激發(fā)事件。這通常通過調用事件的委托來完成
  • 事件是一種特殊的多播委托,僅可以從聲明事件的類(或派生類)或結構(發(fā)布服務器類)中對其進行調用

下列關鍵字應用于事件:

image.png
// 事件數(shù)據(jù)類
// SampleEventArgs 是一個事件參數(shù)類,包含了事件攜帶的信息
public class SampleEventArgs 
{
    public string Message { get; set; }
}

// 發(fā)布者
public class Publisher
{
    // 事件的處理程序委托類型 SampleEventHandler,用于定義事件處理程序的方法簽名
    public delegate void SampleEventHandler(object sender, SampleEventArgs e)

    // 聲明一個SampleEventHandler 類型的事件
    public event SampleEventHandler SampleEvent;

    // 受保護的虛擬方法,用于觸發(fā)事件
    protected virtual void RaiseSampleEvent()
    {
        // 在適當?shù)臅r候觸發(fā)事件
        SampleEvent?.Invoke(this, new SampleEventArgs { Message = "Event occurred!" });
    }
}

// 訂閱者
class Subscriber
{
    // 事件處理程序方法,用于訂閱事件
    public void HandleSampleEvent(object sender, SampleEventArgs e)
    {
        Console.WriteLine($"Received message: {e.Message}");
    }
}

class Program
{
    static void Main()
    {
        // 在其他類中創(chuàng)建 Publisher 類的實例
        Publisher publisher = new Publisher();

        // 創(chuàng)建 Subscriber 類的實例
        Subscriber subscriber = new Subscriber();

        // 使用 += 運算符訂閱事件
        publisher.SampleEvent += subscriber.HandleSampleEvent;

        // 在適當?shù)臅r候調用 RaiseSampleEvent 方法,觸發(fā)事件
        publisher.RaiseSampleEvent();

        // 取消訂閱事件
        publisher.SampleEvent -= subscriber.HandleSampleEvent; 
    }
}

// Output
// Received message: Event occurred!

event: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/event

extern

  • extern 修飾符用于聲明在外部實現(xiàn)的方法,在當前 C# 代碼中并未提供方法的實現(xiàn),而是該方法的實現(xiàn)在外部的其他語言中
  • extern 修飾符的常見用法是在使用 Interop 服務調入非托管代碼時與 DllImport 特性一起使用,同時,還必須將方法聲明為 static
  • extern 關鍵字還可以定義外部程序集別名,使得可以從單個程序集中引用同一組件的不同版本
//using System.Runtime.InteropServices;
class ExternTest
{
    // 程序使用從 User32.dll 庫導入的 MessageBox 方法, 搭配 static 一起使用
    [DllImport("User32.dll", CharSet=CharSet.Unicode)]
    public static extern int MessageBox(IntPtr h, string m, string c, int type);

    static int Main()
    {
        string myString;
        Console.Write("Enter your message: ");
        myString = Console.ReadLine();
        return MessageBox((IntPtr)0, myString, "My Message Box", 0);
    }
}

extern: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/extern

new

  • new 關鍵字可以顯式隱藏從基類繼承的成員,在派生類中使用 new 關鍵字可以隱藏基類中具有相同名稱的成員,主要用于隱藏基類共享名稱的字段、屬性、方法、索引器等,但要注意隱藏和覆蓋
  • 類或結構中引入的索引器會隱藏具有相同簽名的所有基類索引器
  • new 關鍵字還可用于創(chuàng)建類型的實例或用作泛型類型約束
  • 同一成員同時使用 new 和 override 是錯誤的做法,因為這兩個修飾符的含義互斥
    • new 修飾符會用同樣的名稱創(chuàng)建一個新成員并使原始成員變?yōu)殡[藏
    • override 修飾符會擴展繼承成員的實現(xiàn)
class BaseClass
{
    public void Display()
    {
        Console.WriteLine("BaseClass Display");
    }
}

class DerivedClass : BaseClass
{
    // 1. 使用 new 關鍵字隱藏基類的 Display 方法
    public new void Display()
    {
        Console.WriteLine("DerivedClass Display");
    }
}

// 2. 創(chuàng)建實例,實例化類或結構,并為其分配內存
MyClass obj = new MyClass();

// 3. 聲明和分配數(shù)組,為數(shù)組分配內存空間
int[] numbers = new int[5];

// 4. 泛型類型的實例化,new 關鍵字為類型參數(shù)創(chuàng)建新的實例
List<int> myList = new List<int>();

// 5. 在泛型約束中,new() 約束表示泛型類型必須具有公共的無參數(shù)構造函數(shù)
public class Example<T> where T : new()
{
    // T 必須有無參數(shù)構造函數(shù)
}

注意: 當 相同名稱的新成員 被聲明為 不可調用類型 且 基類 將 其聲明為一種方法, 則 新引入的成員不會隱藏基類中的同名成員

class BaseClass
{
    public void Method()
    {
        Console.WriteLine("BaseClass Method");
    }
}

class DerivedClass : BaseClass
{
    // 在派生類中引入一個名為 Method 的字段
    // 該字段是一個委托類型,不可調用
    public Action Method;

    public new void Method()
    {
        Console.WriteLine("DerivedClass Method");
    }
}

class Program
{
    static void Main()
    {
        DerivedClass derivedInstance = new DerivedClass();

        // 調用基類的方法
        derivedInstance.Method();  // 輸出 "BaseClass Method"

        // 調用派生類中引入的字段(不可調用)
        derivedInstance.Method?.Invoke();  // 僅編譯通過,但實際上不會執(zhí)行任何操作
    }
}

new: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/new-modifier

override

  • override 關鍵字用于表示一個方法、屬性、索引器或事件在派生類中重寫基類中的同名成員
  • override 方法提供從基類繼承的方法的新實現(xiàn),通過 override 聲明重寫的方法稱為重寫基方法
    • override 方法必須具有與重寫基方法相同的簽名(名稱、返回類型、參數(shù)列表等)
    • override 方法支持協(xié)變返回類型
    • 不能重寫非虛方法或靜態(tài)方法
    • 重寫基方法必須是 virtual、abstract 或 override
  • override 聲明不能更改 virtual 方法的可訪問性
  • 不能使用 new、static 或 virtual 修飾符修改 override 方法
  • 重寫屬性聲明必須指定與繼承的屬性完全相同的訪問修飾符、類型和名稱
    • 只讀重寫屬性支持協(xié)變返回類型
    • 重寫屬性必須為 virtual、abstract 或 override
class BaseClass
{
   public virtual int Value
    {
        get { return 42; }
    }

    public virtual void Display()
    {
        Console.WriteLine("BaseClass Display");
    }

    public virtual int this[int index]
    {
        get { return index * 2; }
    }

    public virtual event EventHandler MyEvent;
}

class DerivedClass : BaseClass
{
   // 1. 屬性的重寫:使用 override 修飾符重寫基類中的屬性
    public override int Value
    {
        get { return 10; }
    }
    // 2. 方法的重寫 :使用 override 修飾符重寫基類中的方法
    public override void Display()
    {
        Console.WriteLine("DerivedClass Display");
    }
   // 3. 索引器的重寫:使用 override 修飾符重寫基類中的索引器
    public override int this[int index]
    {
        get { return index * 10; }
    }
    // 4. 事件的重寫:使用 override 修飾符重寫基類中的事件
    public override event EventHandler MyEvent;
}


override: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/override

readonly

  • readonly 關鍵字用于聲明只讀字段。只讀字段是在聲明時或同一個類的構造函數(shù)中被賦值,并且在其生命周期內不可更改的字段,一旦賦值,字段的值在之后不能再被修改

    • 在聲明中初始化變量時賦值
    • 在包含實例字段聲明的類的實例構造函數(shù)中賦值
    • 在包含靜態(tài)字段聲明的類的靜態(tài)構造函數(shù)中賦值
    • 構造函數(shù)退出后,不能分配 readonly 字段。 此規(guī)則對于值類型和引用類型具有不同的含義:
      • 由于值類型直接包含數(shù)據(jù),因此屬于 readonly 值類型的字段不可變
      • 由于引用類型包含對其數(shù)據(jù)的引用,因此屬于 readonly 引用類型的字段必須始終引用同一對象,該對象是可變的, readonly 并不能阻止通過引用類型的實例修改實例內部的數(shù)據(jù)
  • 使用 readonly 修飾符來聲明實例成員不會修改結構的狀態(tài)

  • 在 readonly struct 類型定義中,readonly 指示結構類型是不可變的

  • 在結構類型內的實例成員聲明中,readonly 指示實例成員不修改結構的狀態(tài)

  • ref readonly 方法返回實現(xiàn)對只讀字段的引用,只允許在只讀字段上創(chuàng)建只讀引用,而不違反字段的只讀性

// 即使在類構造函數(shù)中給字段 MyReadOnlyField 賦了值,也無法在方法 TestChange 中更改其值
public class MyClass
{
    // 只讀字段
    public readonly int MyReadOnlyField;

    // 構造函數(shù)中初始化只讀字段
    public MyClass(int value)
    {
        MyReadOnlyField = value;
    }

    // 嘗試在其他方法中修改只讀字段(編譯錯誤)
    public void TestChange()
    {
        // 下面這行會導致編譯錯誤:
        // MyReadOnlyField = 42;
    }
    // 方法返回只讀字段的只讀引用
    public ref readonly int GetReadOnlyField()
    {
        return ref MyReadOnlyField;
    }
}
// 使用 readonly 修飾符來聲明實例成員不會修改結構的狀態(tài)
public readonly double Sum()
{
    return X + Y;
}

// ref readonly
class Program
{
    static void Main()
    {
        // 創(chuàng)建 MyClass 實例
        MyClass myInstance = new MyClass(42);

        // 獲取只讀字段的只讀引用
        ref readonly int readOnlyRef = ref myInstance.GetReadOnlyField();

        // 通過只讀引用訪問只讀字段
        Console.WriteLine($"Value of MyReadOnlyField: {readOnlyRef}");
    }
}

readonly 字段只能在構造函數(shù)中進行賦值,并且只有在構造函數(shù)上下文中,將 readonly 字段作為 out 或 ref 參數(shù)傳遞才有效

public class MyClass
{
    // 只讀字段
    public readonly int MyReadOnlyField;

    // 構造函數(shù)中初始化只讀字段
    public MyClass(int value)
    {
        MyReadOnlyField = value;

        // 將只讀字段作為 out 參數(shù)傳遞給方法
        MethodWithOutParameter(out MyReadOnlyField);
    }

    // 方法接收只讀字段作為 out 參數(shù)
    private void MethodWithOutParameter(out int value)
    {
        // 這里可以使用 value,因為它是 out 參數(shù)
        value = 100;
    }
}
// 在構造函數(shù)上下文中,readonly 字段的賦值是允許的
// 在其他上下文中, 嘗試修改 readonly 字段的值將導致編譯錯誤

**備注: **

  • readonly 關鍵字不同于 const 關鍵字,const 字段只能在該字段的聲明中初始化
  • 可以在字段聲明和任何構造函數(shù)中多次分配 readonly 字段
  • 根據(jù)所使用的構造函數(shù),readonly 字段可能具有不同的值
  • const 字段是編譯時常量, readonly 字段是可用于運行時常量
public class SamplePoint
{
    public int x;
    // Initialize a readonly field
    public readonly int y = 25;
    public readonly int z;

    public SamplePoint()
    {
        // Initialize a readonly instance field
        z = 24;
    }
    // 根據(jù)所使用的構造函數(shù),readonly 字段可能具有不同的值
    public SamplePoint(int p1, int p2, int p3)
    {
        x = p1;
        y = p2;
        z = p3;
    }

    public static void Main()
    {
        SamplePoint p1 = new SamplePoint(11, 21, 32);   // OK
        Console.WriteLine($"p1: x={p1.x}, y={p1.y}, z={p1.z}");
        SamplePoint p2 = new SamplePoint();
        p2.x = 55;   // OK
        Console.WriteLine($"p2: x={p2.x}, y={p2.y}, z={p2.z}");
    }
    /*
     Output:
        p1: x=11, y=21, z=32
        p2: x=55, y=25, z=24
    */
}

readonly: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/readonly

sealed

  • sealed 關鍵字用于阻止其他類繼承某個類或結構,或者阻止某個方法被子類重寫,防止派生類替代基類的特定虛方法或屬性
  • 將 abstract 修飾符與 密封類 結合使用是錯誤的,抽象類必須由提供抽象方法或屬性的實現(xiàn)的類來繼承,而密封類 會阻止 子類的重寫和實現(xiàn),
  • sealed 阻止重寫的基類方法或屬性必須是 virtual
  • 應用于方法或屬性時,sealed 修飾符必須始終與 override 結合使用,因為結構是隱式密封的,所以無法繼承它們
// 1. 阻止類的繼承 :防止其他類繼承指定的類或結構
public sealed class SealedClass
{
    // 類的成員和方法
}

// 2. 阻止方法的重寫:阻止子類重寫指定的虛擬方法或抽象方法
public class BaseClass
{
    // 虛擬方法
    public virtual void MyMethod()
    {
        // 實現(xiàn)
    }
}

public class DerivedClass : BaseClass
{
    // 使用 sealed 阻止重寫
    public sealed override void MyMethod()
    {
        // 子類的實現(xiàn)
    }
}

sealed:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/sealed

static

  • static 修飾符用于聲明靜態(tài)成員,靜態(tài)成員屬于類而不是類的實例
  • static 修飾符可用于聲明 static 類,若 static 關鍵字應用于類,則類的所有成員都必須為 static
  • 可以將 static 修飾符添加到字段、方法、屬性、運算符、事件和構造函數(shù)
  • 類、接口和 static 類可以具有 static 構造函數(shù)
  • static 修飾符不能用于索引器或終結器
  • static 修飾符添加到 本地函數(shù),靜態(tài)本地函數(shù)無法捕獲局部變量或實例狀態(tài)
  • static 修飾符添加到 Lambda 表達式匿名方法, 靜態(tài)Lambda 表達式或匿名方法無法捕獲局部變量或實例狀態(tài)
  • 不可以使用 this 引用 static 方法或屬性訪問器
// 1. 聲明靜態(tài)類且只含靜態(tài)方法,靜態(tài)字段
static class CompanyEmployee
{
     public static int StaticField = 42;
    public static void DoSomething() { /*...*/ }
    public static void DoSomethingElse() { /*...*/  }
}

// 2. 常數(shù)或類型聲明是隱式的 static 成員,不能通過實例引用 static 成員,但可以通過類型名稱引用
public class MyBaseC
{
    public struct MyStruct
    {
        public static int x = 100;
    }
}
// 注意:要引用 static 成員 x,除非可從相同范圍訪問該成員,否則請使用完全限定的名稱 MyBaseC.MyStruct.x
Console.WriteLine(MyBaseC.MyStruct.x);

// 3. 靜態(tài)構造函數(shù): 使用 static 聲明的構造函數(shù)是類級別的構造函數(shù),在類被使用之前執(zhí)行一次
public class MyClass
{
    static MyClass()
    {
        Console.WriteLine("Static constructor called.");
    }
}

// 4.靜態(tài)初始化
// 使用尚未聲明的 static 字段來初始化另一個 static 字段, 在向 static 字段顯式賦值之后才會定義結果
class Test
{
    static int x = y;
    static int y = 5;

    static void Main()
    {
        Console.WriteLine(Test.x);
        Console.WriteLine(Test.y);

        Test.x = 99;
        Console.WriteLine(Test.x);
    }
}
/*
Output:
    0
    5
    99
*/

使用靜態(tài)字段和方法的情況舉例:

  • 假定此類包含計數(shù)員工的方法 AddEmployee()和存儲員工人數(shù)的字段 employeeCounter
  • AddEmployee()方法和 employeeCounter 字段均不屬于任何一個員工實例, 相反,它們屬于全體員工這個類。 應將其聲明為該類的 static 成員
public class Employee4
{
    public string id;
    public string name;

    public Employee4()
    {
    }

    public Employee4(string name, string id)
    {
        this.name = name;
        this.id = id;
    }

    public static int employeeCounter;

    public static int AddEmployee()
    {
        return ++employeeCounter;
    }
}

class MainClass : Employee4
{
    static void Main()
    {
        Console.Write("Enter the employee's name: ");
        string name = Console.ReadLine();
        Console.Write("Enter the employee's ID: ");
        string id = Console.ReadLine();

        // Create and configure the employee object.
        Employee4 e = new Employee4(name, id);
        Console.Write("Enter the current number of employees: ");
        string n = Console.ReadLine();
        Employee4.employeeCounter = Int32.Parse(n);
        Employee4.AddEmployee();

        // Display the new information.
        Console.WriteLine($"Name: {e.name}");
        Console.WriteLine($"ID:   {e.id}");
        Console.WriteLine($"New Number of Employees: {Employee4.employeeCounter}");
    }
}
/*
Input:
Matthias Berndt
AF643G
15
 *
Sample Output:
Enter the employee's name: Matthias Berndt
Enter the employee's ID: AF643G
Enter the current number of employees: 15
Name: Matthias Berndt
ID:   AF643G
New Number of Employees: 16
*/

static : https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/static

unsafe

  • unsafe 關鍵字表示不安全上下文,允許在不安全上下中使用指針和直接內存訪問
// 1. 指針聲明和使用
unsafe
{
    int x = 10;
    int* ptr = &x;
    Console.WriteLine(*ptr); // 通過指針訪問變量的值
}
// 2. 固定語句: fixed 語句用于創(chuàng)建指向托管變量的指針,以便在不受垃圾回收器干擾的情況下進行內存操作
unsafe
{
    fixed (int* ptr = &array[0])
    {
        // 使用 ptr 操作數(shù)組
    }
}
// 3. 指針算術
unsafe
{
    int[] array = { 1, 2, 3, 4, 5 };
    fixed (int* ptr = &array[0])
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine(*(ptr + i)); // 指針算術
        }
    }
}
// 4. 結構固定
struct MyStruct
{
    public int X;
    public int Y;
}

unsafe
{
    MyStruct myStruct;
    fixed (int* ptrX = &myStruct.X, ptrY = &myStruct.Y)
    {
        // 使用指針訪問結構的字段
    }
}

unsafe:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/unsafe

virtual

  • virtual 關鍵字用于修改方法、屬性、索引器或事件聲明,并使它們可以在派生類中被重寫,在面向對象編程中,這是實現(xiàn)多態(tài)性的一種方式
  • 虛擬成員的實現(xiàn)可由派生類中的替代成員更改
  • 調用虛擬方法時,將為替代的成員檢查該對象的運行時類型:會調用大部分派生類中的該替代成員,如果沒有派生類替代該成員,則它可能是原始成員
  • 默認情況下,方法是非虛擬的,不能替代非虛方法
  • 靜態(tài)屬性上使用 virtual 修飾符是錯誤的
  • 通過包括使用 override 修飾符的屬性聲明,可在派生類中替代虛擬繼承屬性
public class MyBaseClass
{
    // 1. 虛屬性:使用 virtual 關鍵字聲明虛屬性
    public virtual int MyProperty { get; set; }
    // 2. 虛方法:使用 virtual 關鍵字聲明虛方法
    public virtual void MyMethod()
    {
        Console.WriteLine("Base class method");
    }
}

public class MyDerivedClass : MyBaseClass
{
    // 使用 override 關鍵字重寫基類的虛屬性
    public override int MyProperty
    {
        get { return base.MyProperty * 2; }
        set { base.MyProperty = value; }
    }
    // 使用 override 關鍵字重寫基類的虛方法
    public override void MyMethod()
    {
        Console.WriteLine("Derived class method");
    }
}

virtual:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/virtual

volatile

  • volatile 是一個關鍵字,用于指示一個字段可能會被多個線程同時訪問,因此在訪問該字段時不應該進行編譯器優(yōu)化或緩存
  • volatile 修飾的字段告訴編譯器不要對這個字段進行優(yōu)化,而是直接從內存中讀取或寫入這個字段的值
  • volatile 關鍵字可應用于以下類型的字段:
    • 引用類型
    • 指針類型(在不安全的上下文中)。 請注意,雖然指針本身可以是可變的,但是它指向的對象不能是可變的。 換句話說,不能聲明“指向可變對象的指針”
    • 簡單類型,如 sbyte、byte、short、ushort、intuint、char、floatbool。
    • 具有以下基本類型之一的 enum 類型:byte、sbyte、short、ushortintuint。
    • 已知為引用類型的泛型類型參數(shù)
    • IntPtrUIntPtr
  • volatile 關鍵字只能應用于 class 或 struct 的字段。 不能將局部變量聲明為 volatile
using System;
using System.Threading;

public class SharedResource
{
    // 使用 volatile 關鍵字聲明共享字段
    public volatile int sharedValue = 0;
}

class Program
{
    static void Main()
    {
        SharedResource sharedResource = new SharedResource();

        // 創(chuàng)建一個線程修改共享字段的值
        Thread modifyThread = new Thread(() =>
        {
            for (int i = 0; i < 10; i++)
            {
                // 修改共享字段的值
                sharedResource.sharedValue = i;
                Thread.Sleep(500); // 模擬耗時操作
            }
        });

        // 創(chuàng)建另一個線程讀取共享字段的值
        Thread readThread = new Thread(() =>
        {
            while (sharedResource.sharedValue < 9)
            {
                // 讀取共享字段的值
                Console.WriteLine($"Shared Value: {sharedResource.sharedValue}");
                Thread.Sleep(1000); // 模擬耗時操作
            }
        });

        // 啟動兩個線程
        modifyThread.Start();
        readThread.Start();

        // 等待兩個線程完成
        modifyThread.Join();
        readThread.Join();
    }
}

volatile:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/volatile

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • C# 修飾符一 在 C# 中,修飾符(modifiers)是用于修改類型、成員、方法等聲明的關鍵字,它們提供了額外...
    三千闌干閱讀 128評論 0 1
  • 記錄自己在學習c#遇到的知識點(容易忽略容易忘記得,或一些小技巧)[持續(xù)更新] 前言: 在大部分應用情況下,"效率...
    wwmin_閱讀 1,257評論 0 51
  • 0、獲取輸入?yún)?shù)的實際名稱 使用nameof 1、枚舉的轉換 2、C#判斷對象類型 3、用反射動態(tài)綁定控件事件 容...
    宅玖蔡閱讀 1,176評論 0 0
  • Linq 基礎 語言集成查詢 (LINQ) 是一系列直接將查詢功能集成到 C# 語言的技術統(tǒng)稱 LINQ 最明顯...
    三千闌干閱讀 133評論 0 1
  • 參考: const : https://docs.microsoft.com/zh-cn/dotnet/cshar...
    86a262e62b0b閱讀 589評論 0 0

友情鏈接更多精彩內容