C# 修飾符一
在 C# 中,修飾符(modifiers)是用于修改類型、成員、方法等聲明的關(guān)鍵字,它們提供了額外的信息和控制
C# 中,修飾符主要包括 訪問修飾符、參數(shù)修飾符、其他修飾符等
訪問修飾符
訪問修飾符是關(guān)鍵字,用于指定成員或類型已聲明的可訪問性
訪問修飾符主要是用于控制 類、成員、方法等的訪問權(quán)限,包括:
默認可訪問性規(guī)則
對于頂級類型(類、結(jié)構(gòu)、接口等),默認是 internal(在同一程序集內(nèi)可見);對于成員(字段、方法等),默認是 private(僅在類內(nèi)可見)
// 默認情況下,頂級類型是 internal
class MyClass
{
// 這里的類成員默認是 private
int myField;
// 默認情況下,成員是 private
void MyMethod() { }
}
// 明確指定類的可訪問性為 public
public class MyPublicClass
{
// 這里的類成員默認是 private
int myField;
// 默認情況下,成員是 private
void MyMethod() { }
}
class AnotherClass
{
// 明確指定成員的可訪問性為 public
public int MyPublicField;
// 明確指定成員的可訪問性為 public
public void MyPublicMethod() { }
}
可訪問級別
如圖所示,訪問修飾符的可訪問權(quán)限由上到下依次變小

注意:
- 除使用 protected internal 或private protected 組合的情況外,一個成員或類型僅允許一個訪問修飾
- 命名空間中不允許出現(xiàn)訪問修飾符, 命名空間沒有任何訪問限制
- 其他類型的成員的嵌套類型可以具有如下圖所示的可訪問性
- 具有 private 可訪問性的 interface 成員必須具有默認的實現(xiàn)

- 訪問限制

- 如果使用
record關(guān)鍵字修飾符修改類或結(jié)構(gòu),則允許相同的訪問修飾符;
此外,使用record修飾符,類和結(jié)構(gòu)的默認成員可訪問性仍然為private
public record Person
{
// record 類型的默認行為是將成員的可訪問性設(shè)置為 private
string FirstName { get; init; }
string LastName { get; init; }
// 默認情況下,record 類型的成員可訪問性是 private , 但仍可以顯式指定訪問修飾符,如public
public int Ago {get; init;}
// 自定義構(gòu)造函數(shù)
public Person(string firstName, string lastName, int ago)
{
FirstName = firstName;
LastName = lastName;
Ago = ago;
}
}
class Program
{
static void Main()
{
// 創(chuàng)建記錄類型實例
Person person = new Person("John", "Doe",18);
// 訪問記錄類型的屬性,因為屬性的可訪問性是 private
Console.WriteLine($"First Name: {person.FirstName}, Last Name: {person.LastName}, Ago:{person.ago}");
}
}
- 嵌套類型的可訪問性依賴于它的可訪問域,該域是由已聲明的成員可訪問性和直接包含類型的可訪問域這二者共同確定的。 但是,嵌套類型的可訪問域不能超出包含類型的可訪問域
// 外部類型,有一個嵌套類型 NestedClass
public class OuterClass
{
// 外部類型的成員,默認是 private
private int outerField;
// 外部類型的成員方法,默認是 private
private void OuterMethod() { }
// 嵌套類型,默認可訪問性取決于外部類型的可訪問性
public class NestedClass
{
// 嵌套類型的成員,可以訪問外部類型的成員
public void AccessOuterMembers(OuterClass outer)
{
// 可以訪問外部類型的 private 成員
outer.outerField = 42;
// 可以調(diào)用外部類型的 private 方法
outer.OuterMethod();
}
}
}
class Program
{
static void Main()
{
// 外部類型的實例
OuterClass outerInstance = new OuterClass();
// 嵌套類型的實例
OuterClass.NestedClass nestedInstance = new OuterClass.NestedClass();
// 在 Main 方法中,無法直接訪問嵌套類型的成員
// 這是因為嵌套類型的可訪問域不能超過外部類型的可訪問域
// 下面的代碼將無法通過編譯
// nestedInstance.AccessOuterMembers(outerInstance);
}
}
關(guān)鍵字
1. public
- public 公共訪問是允許的最高訪問級別,對訪問公共成員沒有限制
class PointTest
{
public int x;
public int y;
}
class Program
{
static void Main()
{
var p = new PointTest();
// 直接從 Program 訪問 PointTest 的公共成員 x 和 y
p.x = 10;
p.y = 15;
Console.WriteLine($"x = {p.x}, y = {p.y}");
}
}
// Output: x = 10, y = 15
// 如果將 `public` 訪問級別更改為 private 或 protected,則會收到錯誤消息:
// “xxx”不可訪問,因為它受保護級別限制。
public:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/public
2. private
- 私有訪問是允許的最低訪問級別。 私有成員只有在聲明它們的類和結(jié)構(gòu)體中才是可訪問的
- 同一體中的嵌套類型也可以訪問那些私有成員
- 在聲明私有成員的類或結(jié)構(gòu)外引用它會導(dǎo)致編譯時錯誤
class Employee2
{
private readonly string _name = "FirstName, LastName";
private readonly double _salary = 100.0;
public string GetName()
{
return _name;
}
public double Salary
{
get { return _salary; }
}
}
class PrivateTest
{
static void Main()
{
var e = new Employee2();
// 成員變量是 private
// 所以不能進行如下訪問
// string n = e._name;
// double s = e._salary;
// 通過方法間接訪問
string n = e.GetName();
double s = e.Salary;
}
}
private:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/private
3. protected
- 受保護成員在其所在的類中可由派生類實例訪問
+只有在通過派生類類型進行訪問時,基類的受保護成員在派生類中才是可訪問的
class A
{
protected int x = 123;
}
class B : A
{
static void Main()
{
var a = new A();
var b = new B();
// 不可以通過 A 的實例 a 訪問 保護屬性 x
// a.x = 10;
// 可以通過 A 的派生類 b 訪問 包含保護屬性 x
b.x = 10;
}
}
protected:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/protected
4. internal
- 只有在同一程序集的文件中,內(nèi)部類型或成員才可訪問
- 內(nèi)部訪問通常用于基于組件的開發(fā),因為它可使一組組件以私有方式進行協(xié)作,而不必向應(yīng)用程序代碼的其余部分公開
- 從定義具有內(nèi)部訪問權(quán)限的類型或成員的程序集外部引用該類型或成員是錯誤的
// 程序集 Assembly1.cs
internal class BaseClass
{
public static int intM = 0;
}
// 程序集 Assembly1.cs
public class BaseClass2
{
internal static int intM = 0;
}
//程序集 Assembly1_a.cs
class TestAccess
{
static void Main()
{
// 1.
// BaseClass 的程序集來源是 Assembly1
// TestAccess 的程序集來源是 Assembly1_a
// TestAccess 相對于BaseClass 來說是外部程序集,無法直接訪問
var myBase = new BaseClass(); // 報錯
// 2.
// 不同程序集下:
// 可以實例化 BaseClass2類,但不能訪問內(nèi)部成員 intM
var myBase = new BaseClass2(); // 成功
BaseClass.intM = 444; // 報錯
}
}
internal:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/internal
5. protected internal
- protected internal 關(guān)鍵字組合是一種成員訪問修飾符
- 可從 當(dāng)前程序集 或 派生自包含類 的類型 訪問 受保護的內(nèi)部成員
- 通過 派生類 訪問 受保護的內(nèi)部成員,前提 通過 派生類類型 的 變量 進行 訪問
- 結(jié)構(gòu)成員不能為 protected internal,因為無法繼承結(jié)構(gòu)
// 程序集 Assembly1.cs
public class BaseClass
{
protected internal int myValue = 0;
}
// 程序集 Assembly1.cs
class TestAccess
{
void Access()
{
// 相同程序集,TestAccess 可以通過new BaseClass 訪問 受保護內(nèi)部成員 myValue
var baseObject = new BaseClass();
baseObject.myValue = 5;
}
}
// 程序集Assembly2.cs
class DerivedClass : BaseClass
{
static void Main()
{
var baseObject = new BaseClass();
var derivedObject = new DerivedClass();
// 不同程序集,無法通過 new BaseClass 的實例去訪問 受保護內(nèi)部成員 myValue
baseObject.myValue = 10; // 報錯
// 不同程序集,通過 派生 DerivedClass 的實例 去訪問 受保護的內(nèi)部成員 myValue
derivedObject.myValue = 10; // ok
}
}
protected internal:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/protected-internal
6.private protected
- private protected 關(guān)鍵字組合是一種成員訪問修飾符(C# 版本 7.2 及更高版本中有效)
- 僅派生自包含類的類型可訪問私有受保護成員,但僅能在其包含程序集中訪問
- 結(jié)構(gòu)成員不能為 private protected,因為無法繼承結(jié)構(gòu)
// 程序集 Assembly1.cs
public class BaseClass
{
private protected int myValue = 0;
}
// 程序集 Assembly1.cs
public class DerivedClass1 : BaseClass
{
void Access()
{
var baseObject = new BaseClass();
// 同一程序集下: 私有受保護成員 myValue 不能通過 BaseClass 的實例訪問,只能 BaseClass 類內(nèi)部訪問或者 BaseClass 的派生類訪問
baseObject.myValue = 5; // 報錯
// DerivedClass1 是 BaseClass 的派生類,可以直接訪問myValue
myValue = 5; // ok
}
}
// 程序集 Assembly2.cs
class DerivedClass2 : BaseClass
{
void Access()
{
//不同一程序集下:無法訪問 其他程序集的 私有受保護 成員
myValue = 10; // 報錯
}
}
private protected:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/private-protected
7. file
- file 上下文關(guān)鍵字是類型修飾符
- file 修飾符將 頂級類型的范圍 和 可見性限制 為 聲明它的 文件
- file 修飾符通常應(yīng)用于源生成器編寫的類型
- 嵌套在 file類型 中的 任何類型 也僅在聲明 它的文件 中可見,程序集中的其他類型可以使用與 file類型相同的名稱
- file 類型不能是可見性超過 file 范圍的任何成員的返回類型或參數(shù)類型
// In File1.cs:
file interface IWidget
{
int ProvideAnswer();
}
file class HiddenWidget
{
public int Work() => 42;
}
public class Widget : IWidget
{
public int ProvideAnswer()
{
var worker = new HiddenWidget();
return worker.Work();
}
}
// In File2.cs:
// 可以聲明與文件本地類型具有相同名稱的類型, 文件本地類型不可見
// 與HiddenWidget不沖突
public class HiddenWidget
{
public void RunTask()
{
// omitted
}
}
file: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/file
參數(shù)修飾符
- 參數(shù)修飾符 主要用于改變方法參數(shù)行為或傳遞方式的 關(guān)鍵字
- 可以用于方法的參數(shù)列表中
- 提供了不同的傳遞方式或指定了參數(shù)的特殊行為
- 常見的參數(shù)修飾符有:
- in 修飾符
- out 修飾符
- ref 修飾符
- params 修飾符
1. in
in 作為修飾符
- in 修飾符用于指定參數(shù)是只讀的(C# 7.2 引入的一個特性)
- 使用 in 修飾符來傳遞參數(shù)時,表示參數(shù)在方法內(nèi)是只讀的,方法內(nèi)部不能修改該參數(shù)的值
- 當(dāng)傳遞大型結(jié)構(gòu)體或類對象時,使用 in 修飾符可以避免不必要的拷貝操作,提高性能
void MyMethod(in int x)
{
// 在這里不能修改 x 的值
}
int value = 42;
MyMethod(in value);
in 作為 泛型參數(shù)類型
- in 作為泛型參數(shù)類型的一部分通常用于接口和委托,可指定類型參數(shù)是逆變的
- 在泛型接口和委托中使用 in 關(guān)鍵字,使用更泛化的類型替代更具體的類型
- 具有 逆變類型參數(shù) 的接口 使其 方法接受的參數(shù)的類型 可以比 接口類型參數(shù) 指定的類型 派生程度 更小
// 1. 逆變泛型接口
// 逆變 接口
interface IContravariant<in A> { }
// 擴展 逆變 接口
interface IExtContravariant<in A> : IContravariant<A> { }
// 實現(xiàn) 逆變 接口
class Sample<A> : IContravariant<A> { }
class Program
{
static void Test()
{
IContravariant<Object> iobj = new Sample<Object>();
IContravariant<String> istr = new Sample<String>();
// 可以把 iobj 賦值 給 istr
// 因為 IContravariant 是逆變接口,類型可以逆變
istr = iobj;
}
}
// 2. 逆變泛型委托
// 逆變 委托
public delegate void DContravariant<in A>(A argument);
// 匹配 委托簽名 的方法
public static void SampleControl(Control control){ }
public static void SampleButton(Button button){ }
public void Test()
{
// 使用方法實例化委托
DContravariant<Control> dControl = SampleControl;
DContravariant<Button> dButton = SampleButton;
// 可以把 dControl 賦值 給 dButton
// 因為 DContravariant 是逆變委托,類型可以逆變
dButton = dControl;
// 調(diào)用委托
dButton(new Button());
}
兩種用法之間的關(guān)鍵區(qū)別在于上下文:一個是方法參數(shù)的上下文,一個是泛型接口或委托的上下文
in: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/in-generic-modifier
2. out
out 作為修飾符
- out 修飾符 用于指示參數(shù)是一個輸出參數(shù),允許方法修改參數(shù)的值并將其傳遞回調(diào)用方
- 調(diào)用方在調(diào)用方法之前不需要為 out 參數(shù)分配值
- 方法內(nèi)部必須在退出之前為 out 參數(shù)分配一個值
void MyMethod(out int result)
{
// 在這里必須為 result 分配一個值
result = 42;
}
// 調(diào)用 MyMethod 時,不需要初始化 result,因為它是 out 參數(shù)
int outputValue;
MyMethod(out outputValue);
// 此時 outputValue 的值為 42
out 作為 泛型參數(shù)類型
- out 作為泛型參數(shù)類型的一部分通常用于接口和委托,可指定類型參數(shù)是協(xié)變的
- 在泛型接口和委托中使用 out 關(guān)鍵字,可以 使得泛型參數(shù)類型可以被替代為其派生類型
- 具有協(xié)變類型參數(shù)的接口使其方法返回的類型可以比類型參數(shù)指定的類型派生程度更大
// 1. 協(xié)變泛型接口
// 協(xié)變 接口
interface ICovariant<out R> { }
// 擴展 協(xié)變 接口
interface IExtCovariant<out R> : ICovariant<R> { }
// 實現(xiàn) 協(xié)變 接口
class Sample<R> : ICovariant<R> { }
class Program
{
static void Test()
{
ICovariant<Object> iobj = new Sample<Object>();
ICovariant<String> istr = new Sample<String>();
// 可以將 istr 賦值給 iobj
// Covariant 接口是 協(xié)變
iobj = istr;
}
}
// 2. 協(xié)變泛型 委托
// 協(xié)變 委托
public delegate R DCovariant<out R>();
// 匹配 委托簽名 的方法
public static Control SampleControl()
{
return new Control();
}
public static Button SampleButton()
{
return new Button();
}
public void Test()
{
// 使用方法 實例化 委托
DCovariant<Control> dControl = SampleControl;
DCovariant<Button> dButton = SampleButton;
// 把 dButton 賦值給 dControl
// DCovariant委托是協(xié)變的
dControl = dButton;
// 調(diào)用委托
dControl();
}
兩種用法之間的關(guān)鍵區(qū)別在于上下文:一個是方法參數(shù)的上下文,一個是泛型接口或委托的上下文
out : https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/out-generic-modifier
逆變和協(xié)變
協(xié)變(Covariance)和逆變(Contravariance)是泛型類型參數(shù)在繼承關(guān)系中的兩個重要概念,它們指定了在派生類型和基類型之間如何處理泛型類型參數(shù)
- 協(xié)變允許泛型類型參數(shù)在派生類型中使用更為具體的類型
- 逆變允許泛型類型參數(shù)在派生類型中使用更為抽象的類型
- 協(xié)變使用 out 關(guān)鍵字,逆變使用 in 關(guān)鍵字
1. 逆變(Contravariance)
- 表示泛型類型參數(shù)能夠在派生類型中使用更為抽象的類型(基類型)
- 在 C# 中,逆變使用 in 關(guān)鍵字來標(biāo)記泛型參數(shù),同樣通常出現(xiàn)在接口和委托中
- 逆變允許將一個泛型類型參數(shù)更為抽象的類型傳遞給一個期望派生類型的情況
// 示例中,T 是逆變的
delegate void MyDelegate<in T>(T item);
// 可以將 Action<Base> 賦值給 MyDelegate<Derived>
MyDelegate<Derived> myAction = new Action<Base>(someBase => { /* 實現(xiàn) */ });
2. 協(xié)變(Covariance)
- 協(xié)變表示泛型類型參數(shù)能夠在派生類型中使用更為具體的類型(派生類型)
- 在 C# 中,協(xié)變使用 out 關(guān)鍵字來標(biāo)記泛型參數(shù),通常出現(xiàn)在接口和委托中
- 協(xié)變允許將一個泛型類型參數(shù)更為具體的類型傳遞給一個期望基類型的情況
// 示例中,T 是協(xié)變的
interface IMyInterface<out T>
{
T GetItem();
}
// 可以將 MyImplementation<Derived> 賦值給 IMyInterface<Base>
IMyInterface<Base> myObj = new MyImplementation<Derived>();
3. ref
- ref 關(guān)鍵字用于將參數(shù)傳遞作為引用
- 通過 ref,方法可以修改調(diào)用方提供的變量的值
- 調(diào)用方必須初始化變量,且在調(diào)用方法時,參數(shù)需要使用 ref 關(guān)鍵字
void ModifyValue(ref int x)
{
x = x * 2;
}
int value = 5;
ModifyValue(ref value);
// 此時 value 的值為 10
4. params
- params 關(guān)鍵字用于表示一個可變數(shù)量的參數(shù)
- 該參數(shù)必須是數(shù)組類型,而調(diào)用方可以提供不同數(shù)量的參數(shù)
int Sum(params int[] numbers)
{
int sum = 0;
foreach (int num in numbers)
{
sum += num;
}
return sum;
}
int total = Sum(1, 2, 3, 4, 5);
// 此時 total 的值為 15
參數(shù)修飾符
- 參數(shù)修飾符 主要用于改變方法參數(shù)行為或傳遞方式的 關(guān)鍵字
- 可以用于方法的參數(shù)列表中
- 提供了不同的傳遞方式或指定了參數(shù)的特殊行為
- 常見的參數(shù)修飾符有:
- in 修飾符
- out 修飾符
- ref 修飾符
- params 修飾符
1. in
in 作為修飾符
- in 修飾符用于指定參數(shù)是只讀的(C# 7.2 引入的一個特性)
- 使用 in 修飾符來傳遞參數(shù)時,表示參數(shù)在方法內(nèi)是只讀的,方法內(nèi)部不能修改該參數(shù)的值
- 當(dāng)傳遞大型結(jié)構(gòu)體或類對象時,使用 in 修飾符可以避免不必要的拷貝操作,提高性能
void MyMethod(in int x)
{
// 在這里不能修改 x 的值
}
int value = 42;
MyMethod(in value);
in 作為 泛型參數(shù)類型
- in 作為泛型參數(shù)類型的一部分通常用于接口和委托,可指定類型參數(shù)是逆變的
- 在泛型接口和委托中使用 in 關(guān)鍵字,使用更泛化的類型替代更具體的類型
- 具有 逆變類型參數(shù) 的接口 使其 方法接受的參數(shù)的類型 可以比 接口類型參數(shù) 指定的類型 派生程度 更小
// 1. 逆變泛型接口
// 逆變 接口
interface IContravariant<in A> { }
// 擴展 逆變 接口
interface IExtContravariant<in A> : IContravariant<A> { }
// 實現(xiàn) 逆變 接口
class Sample<A> : IContravariant<A> { }
class Program
{
static void Test()
{
IContravariant<Object> iobj = new Sample<Object>();
IContravariant<String> istr = new Sample<String>();
// 可以把 iobj 賦值 給 istr
// 因為 IContravariant 是逆變接口,類型可以逆變
istr = iobj;
}
}
// 2. 逆變泛型委托
// 逆變 委托
public delegate void DContravariant<in A>(A argument);
// 匹配 委托簽名 的方法
public static void SampleControl(Control control){ }
public static void SampleButton(Button button){ }
public void Test()
{
// 使用方法實例化委托
DContravariant<Control> dControl = SampleControl;
DContravariant<Button> dButton = SampleButton;
// 可以把 dControl 賦值 給 dButton
// 因為 DContravariant 是逆變委托,類型可以逆變
dButton = dControl;
// 調(diào)用委托
dButton(new Button());
}
兩種用法之間的關(guān)鍵區(qū)別在于上下文:一個是方法參數(shù)的上下文,一個是泛型接口或委托的上下文
in: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/in-generic-modifier
2. out
out 作為修飾符
- out 修飾符 用于指示參數(shù)是一個輸出參數(shù),允許方法修改參數(shù)的值并將其傳遞回調(diào)用方
- 調(diào)用方在調(diào)用方法之前不需要為 out 參數(shù)分配值
- 方法內(nèi)部必須在退出之前為 out 參數(shù)分配一個值
void MyMethod(out int result)
{
// 在這里必須為 result 分配一個值
result = 42;
}
// 調(diào)用 MyMethod 時,不需要初始化 result,因為它是 out 參數(shù)
int outputValue;
MyMethod(out outputValue);
// 此時 outputValue 的值為 42
out 作為 泛型參數(shù)類型
- out 作為泛型參數(shù)類型的一部分通常用于接口和委托,可指定類型參數(shù)是協(xié)變的
- 在泛型接口和委托中使用 out 關(guān)鍵字,可以 使得泛型參數(shù)類型可以被替代為其派生類型
- 具有協(xié)變類型參數(shù)的接口使其方法返回的類型可以比類型參數(shù)指定的類型派生程度更大
// 1. 協(xié)變泛型接口
// 協(xié)變 接口
interface ICovariant<out R> { }
// 擴展 協(xié)變 接口
interface IExtCovariant<out R> : ICovariant<R> { }
// 實現(xiàn) 協(xié)變 接口
class Sample<R> : ICovariant<R> { }
class Program
{
static void Test()
{
ICovariant<Object> iobj = new Sample<Object>();
ICovariant<String> istr = new Sample<String>();
// 可以將 istr 賦值給 iobj
// Covariant 接口是 協(xié)變
iobj = istr;
}
}
// 2. 協(xié)變泛型 委托
// 協(xié)變 委托
public delegate R DCovariant<out R>();
// 匹配 委托簽名 的方法
public static Control SampleControl()
{
return new Control();
}
public static Button SampleButton()
{
return new Button();
}
public void Test()
{
// 使用方法 實例化 委托
DCovariant<Control> dControl = SampleControl;
DCovariant<Button> dButton = SampleButton;
// 把 dButton 賦值給 dControl
// DCovariant委托是協(xié)變的
dControl = dButton;
// 調(diào)用委托
dControl();
}
兩種用法之間的關(guān)鍵區(qū)別在于上下文:一個是方法參數(shù)的上下文,一個是泛型接口或委托的上下文
out : https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/out-generic-modifier
逆變和協(xié)變
協(xié)變(Covariance)和逆變(Contravariance)是泛型類型參數(shù)在繼承關(guān)系中的兩個重要概念,它們指定了在派生類型和基類型之間如何處理泛型類型參數(shù)
- 協(xié)變允許泛型類型參數(shù)在派生類型中使用更為具體的類型
- 逆變允許泛型類型參數(shù)在派生類型中使用更為抽象的類型
- 協(xié)變使用 out 關(guān)鍵字,逆變使用 in 關(guān)鍵字
1. 逆變(Contravariance)
- 表示泛型類型參數(shù)能夠在派生類型中使用更為抽象的類型(基類型)
- 在 C# 中,逆變使用 in 關(guān)鍵字來標(biāo)記泛型參數(shù),同樣通常出現(xiàn)在接口和委托中
- 逆變允許將一個泛型類型參數(shù)更為抽象的類型傳遞給一個期望派生類型的情況
// 示例中,T 是逆變的
delegate void MyDelegate<in T>(T item);
// 可以將 Action<Base> 賦值給 MyDelegate<Derived>
MyDelegate<Derived> myAction = new Action<Base>(someBase => { /* 實現(xiàn) */ });
2. 協(xié)變(Covariance)
- 協(xié)變表示泛型類型參數(shù)能夠在派生類型中使用更為具體的類型(派生類型)
- 在 C# 中,協(xié)變使用 out 關(guān)鍵字來標(biāo)記泛型參數(shù),通常出現(xiàn)在接口和委托中
- 協(xié)變允許將一個泛型類型參數(shù)更為具體的類型傳遞給一個期望基類型的情況
// 示例中,T 是協(xié)變的
interface IMyInterface<out T>
{
T GetItem();
}
// 可以將 MyImplementation<Derived> 賦值給 IMyInterface<Base>
IMyInterface<Base> myObj = new MyImplementation<Derived>();
3. ref
- ref 關(guān)鍵字用于將參數(shù)傳遞作為引用
- 通過 ref,方法可以修改調(diào)用方提供的變量的值
- 調(diào)用方必須初始化變量,且在調(diào)用方法時,參數(shù)需要使用 ref 關(guān)鍵字
void ModifyValue(ref int x)
{
x = x * 2;
}
int value = 5;
ModifyValue(ref value);
// 此時 value 的值為 10
4. params
- params 關(guān)鍵字用于表示一個可變數(shù)量的參數(shù)
- 該參數(shù)必須是數(shù)組類型,而調(diào)用方可以提供不同數(shù)量的參數(shù)
int Sum(params int[] numbers)
{
int sum = 0;
foreach (int num in numbers)
{
sum += num;
}
return sum;
}
int total = Sum(1, 2, 3, 4, 5);
// 此時 total 的值為 15