C# 中比較實用的關(guān)鍵字,基礎(chǔ)高頻面試題!
前言
在C#編程中關(guān)鍵字是構(gòu)建邏輯和實現(xiàn)功能的基石,它承載著編程語言的語法規(guī)則和編程智慧。熟練掌握這些基礎(chǔ)高頻關(guān)鍵字對提升編程能力和面試表現(xiàn)至關(guān)重要,它們是日常開發(fā)和解決復(fù)雜問題的關(guān)鍵。
C#訪問修飾符
訪問修飾符的作用
訪問修飾符是用于指定成員或類型的聲明可訪問性的關(guān)鍵字。
四種常見的訪問修飾符
- public(公共的)
- protected(受保護的)
- internal(內(nèi)部的)
- private(私有的)
訪問修飾符的六種組合及其可訪問性級別
- public 訪問不受限制
- protected 訪問限于包含類或派生自包含類的類型
- internal 訪問限于當(dāng)前程序集
- private 訪問限于包含類
- protected internal 訪問限于當(dāng)前程序集或派生自包含類的類型訪問
- private protected 訪問限于包含類或當(dāng)前程序集中包含類的派生類的類型訪問
//包含類
publicclassBaseClass
{
privateprotectedint myValue = 0;
}
//當(dāng)前程序集中包含類的派生類
publicclassDerivedClass1 : BaseClass
{
void Access()
{
var baseObject = new BaseClass();
myValue = 5;
}
}
C#類和結(jié)構(gòu)默認(rèn)訪問修飾符
Internal
C#適用于類和結(jié)構(gòu)訪問修飾符有哪些
public 或 internal
類成員和結(jié)構(gòu)成員的默認(rèn)訪問修飾符為
private
結(jié)構(gòu)成員(包括嵌套的類和結(jié)構(gòu))可以聲明為
public、internal 或 private 注意:結(jié)構(gòu)成員無法聲明為 protected、protected internal 或 private protected,因為結(jié)構(gòu)不支持繼承。
類成員(包括嵌套的類和結(jié)構(gòu))可以聲明為
public、protected internal、protected、internal、private protected 或 private
readonly與const區(qū)別?
readonly關(guān)鍵字(運行時常量):字段可以在聲明或構(gòu)造函數(shù)中初始化,常作為運行時常量使用。
const關(guān)鍵字(編譯時常量):字段只能在該字段的聲明時初始化,常作為編譯時常量使用過。
virtual作用?
詳情閱讀:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/virtual
virtual關(guān)鍵字用于修改方法、屬性、索引器或事件聲明,并使它們可以在派生類中被重寫(使用override關(guān)鍵字對虛方法重寫)。 如下是虛方法聲明和重寫虛方法的示例:
// 基類虛方法聲明
class BaseClass
{
public virtual void Method1()
{
Console.WriteLine("Base - Method1");
}
public virtual void Method2()
{
Console.WriteLine("Base - Method2");
}
}
class DerivedClass : BaseClass
{
// 重寫基類中的虛方法
public override void Method1()
{
Console.WriteLine("Derived - Method1");
}
public new void Method2()
{
Console.WriteLine("Derived - Method2");
}
}
override作用?
擴展或修改繼承的方法、屬性、索引器或事件的抽象或虛擬實現(xiàn)需要 override 修飾符。
static作用?
詳情閱讀:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/static
使用 static 修飾符可聲明屬于類型本身而不是屬于特定對象的靜態(tài)成員。 static 修飾符可用于聲明 static 類。 在類、接口和結(jié)構(gòu)中,可以將 static 修飾符添加到字段、方法、屬性、運算符、事件和構(gòu)造函數(shù)。 static 修飾符不能用于索引器或終結(jié)器。
靜態(tài)類與非靜態(tài)類的區(qū)別?
- 靜態(tài)類無法實例化(換句話說,無法使用 new 運算符創(chuàng)建類類型的變量。 由于不存在任何實例變量,因此可以使用類名本身訪問靜態(tài)類的成員)。
- 靜態(tài)構(gòu)造函數(shù)只調(diào)用一次,在程序所駐留的應(yīng)用程序域的生存期內(nèi),靜態(tài)類會保留在內(nèi)存中(即使用Static修飾的類,應(yīng)用一旦啟用靜態(tài)類就會保留在內(nèi)存中)。
- 靜態(tài)類只包含靜態(tài)成員
- 不能包含實例構(gòu)造函數(shù)。
- 靜態(tài)類會進行密封,因此不能繼承。 它們不能繼承自任何類(除了 Object)。 靜態(tài)類不能包含實例構(gòu)造函數(shù)。 但是,它們可以包含靜態(tài)構(gòu)造函數(shù)。
靜態(tài)成員和非靜態(tài)成員的區(qū)別?
成員主要指的是:字段、方法、屬性、運算符、事件和構(gòu)造函數(shù)等。
- 靜態(tài)成員用static修飾符,非靜態(tài)成員不需要。
- 靜態(tài)成員屬于類所有,非靜態(tài)成員屬于類的實例化對象所有。
- 靜態(tài)方法里不能使用非靜態(tài)成員,非靜態(tài)方法可以使用靜態(tài)成員。
- 每創(chuàng)建一個類的實例,都會在內(nèi)存中為非靜態(tài)成員新分配一塊新的存儲。
- 靜態(tài)成員無論類創(chuàng)建多少個實例,在內(nèi)存中只占同一塊區(qū)域。
靜態(tài)方法的使用場合
- 靜態(tài)方法最適合工具類中方法的定義。
- 靜態(tài)變量適合全局變量的定義。
靜態(tài)方法和非靜態(tài)方法區(qū)別(優(yōu)/缺點)?
優(yōu)點:
- 屬于類級別的,不需要創(chuàng)建對象就可以直接使用。
- 全局唯一,內(nèi)存中唯一,靜態(tài)變量可以唯一標(biāo)識某些狀態(tài)。
- 在類加載時候初始化,常駐在內(nèi)存中,調(diào)用快捷方便。
缺點:
- 靜態(tài)方法不能調(diào)用非靜態(tài)的方法和變量。(非靜態(tài)方法可以任意的調(diào)用靜態(tài)方法/變量)
- 不可以使用 this 引用 static 方法或?qū)傩栽L問器。
sealed 關(guān)鍵字有什么作用?
sealed 關(guān)鍵字用于修飾類、方法或?qū)傩?,表示該類或成員不可被繼承或重寫。如果一個類被聲明為 sealed,其他類不能繼承該類;如果一個方法或?qū)傩员宦暶鳛?sealed,其他類不能重寫該方法或?qū)傩浴?/p>
this 關(guān)鍵字有什么作用?
this 關(guān)鍵字表示當(dāng)前對象的引用,可以用于訪問當(dāng)前對象的成員。它可以用來區(qū)分局部變量和實例變量、在構(gòu)造函數(shù)中調(diào)用其他構(gòu)造函數(shù)、傳遞當(dāng)前對象給其他方法等。
base 關(guān)鍵字有什么作用?
base 關(guān)鍵字表示基類的引用,可以用于訪問基類的成員。它可以用來在子類中調(diào)用基類的構(gòu)造函數(shù)、調(diào)用基類的方法或?qū)傩缘取?/p>
sizeof 關(guān)鍵字有什么作用?
sizeof 運算符返回給定類型的變量所占用的字節(jié)數(shù)。 sizeof 運算符的參數(shù)必須是一個非托管類型的名稱,或是一個限定為非托管類型的類型參數(shù)。
lock 關(guān)鍵字有什么作用?
lock 關(guān)鍵字用于在多線程環(huán)境下對共享資源進行互斥訪問。使用 lock 關(guān)鍵字可以將代碼塊標(biāo)記為臨界區(qū),使得只有一個線程能夠進入臨界區(qū)執(zhí)行代碼。
async 和 await 關(guān)鍵字有什么作用?
async 和 await 關(guān)鍵字用于異步編程。通過使用 async 標(biāo)記方法和 await 等待異步操作完成,可以實現(xiàn)在異步任務(wù)執(zhí)行過程中不阻塞主線程。
delegate 關(guān)鍵字有什么作用?
delegate 關(guān)鍵字用于聲明委托類型,即代表一個或多個方法的對象。使用 delegate 可以實現(xiàn)事件和回調(diào)機制,簡化方法的調(diào)用和管理。
using關(guān)鍵字的作用
- using指令為命名空間創(chuàng)建別名,或?qū)朐谄渌臻g中定義的類型
- using 語句定義一個范圍,在此范圍的末尾將釋放對象資源,實現(xiàn)了IDisposiable的類在using中創(chuàng)建,using結(jié)束后會自定調(diào)用該對象的Dispose方法,釋放資源。
C# 中的 in 關(guān)鍵字有什么作用?
in 關(guān)鍵字用于參數(shù)傳遞時,將參數(shù)按只讀引用傳遞。使用 in 關(guān)鍵字可以提高性能,避免不必要的參數(shù)復(fù)制。
在 C# 中,in 關(guān)鍵字用于將參數(shù)標(biāo)記為輸入?yún)?shù)。它告訴編譯器在方法調(diào)用過程中不會修改該參數(shù)的值,并且可以通過引用傳遞避免對參數(shù)進行復(fù)制。這對于大型結(jié)構(gòu)或?qū)ο髤?shù)非常有用,因為直接引用參數(shù)可以提高性能和內(nèi)存效率。
class Program
{
static void Main(string[] args)
{
int x = 5;
MultiplyByTwo(in x);
Console.WriteLine(x); // 輸出 5
}
static void MultiplyByTwo(in int number)
{
// 無法修改 in 參數(shù)的值
// number *= 2; // 編譯錯誤
// 僅能讀取 in 參數(shù)的值
Console.WriteLine(number * 2); // 輸出 10
}
}
C# 中的 ref 關(guān)鍵字有什么作用?
- 參數(shù)在使用 ref 關(guān)鍵字進行引用傳遞時,必須在方法調(diào)用之前對其進行初始化。
- ref 關(guān)鍵字既可以在進入方法之前初始化參數(shù)的值,也可以在方法內(nèi)部對參數(shù)進行修改。
- ref 參數(shù)在進入方法時保持原始值,并在方法結(jié)束后將值帶回到調(diào)用處。
C# 中的 out 關(guān)鍵字有什么作用?
- 參數(shù)在使用 out 關(guān)鍵字進行引用傳遞時,不需要在方法調(diào)用之前進行初始化。
- out 關(guān)鍵字通常用于表示方法返回多個值的情況,或者用于修改方法外部的變量。
- out 參數(shù)必須在方法內(nèi)部進行初始化,并確保在方法結(jié)束前完成賦值操作。方法內(nèi)部沒有為 out 參數(shù)賦值的情況下,方法調(diào)用將會導(dǎo)致編譯錯誤。
C#中參數(shù)傳遞 ref與out 的區(qū)別?
ref 指定此參數(shù)由引用傳遞,指定的參數(shù)在函數(shù)調(diào)用時必須先初始化(有進有出)。
out 指定此參數(shù)由引用傳遞,指定的參數(shù)在進入函數(shù)時會清空參數(shù)值,因此該參數(shù)必須在調(diào)用函數(shù)內(nèi)部進行初始化賦值操作(無進有出)。
總結(jié):
- ref 和 out 都用于引用傳遞參數(shù)。
- ref 參數(shù)在方法調(diào)用前必須被初始化,而 out 參數(shù)不需要。
- ref 參數(shù)可以保留原始值并在方法內(nèi)部進行修改,而 out 參數(shù)必須在方法內(nèi)部進行初始化賦值。
不能將 in、ref 和 out 關(guān)鍵字用于以下幾種方法:
- 異步方法,通過使用 async 修飾符定義。
- 迭代器方法,包括 yield return 或 yield break 語句。
- 擴展方法的第一個參數(shù)不能有 in 修飾符,除非該參數(shù)是結(jié)構(gòu)。
- 擴展方法的第一個參數(shù),其中該參數(shù)是泛型類型(即使該類型被約束為結(jié)構(gòu))
as和is的區(qū)別
- is 只是做類型兼容判斷,并不執(zhí)行真正的類型轉(zhuǎn)換。返回true或false,不會返回null,對象為null也會返回false。
- as運算符將表達式結(jié)果顯式轉(zhuǎn)換為給定的引用類型或可以為null值的類型。 如果無法進行轉(zhuǎn)換,則as運算符返回 null。
總結(jié):as模式的效率要比is模式的高,因為借助is進行類型轉(zhuǎn)換的化,需要執(zhí)行兩次類型兼容檢查。而as只需要做一次類型兼容,一次null檢查,null檢查要比類型兼容檢查快。
is 運算符
is 運算符用于檢查對象是否是某個特定類型,或者是否可以轉(zhuǎn)換為該類型。它返回一個布爾值 (true 或 false)。
string title = "Hello DotNetGuide";
if (title is string)
{
Console.WriteLine("是 string 類型");
}
else
{
Console.WriteLine("不是 string 類型");
}
if (title is not null)
{
Console.WriteLine("不為 null");
}
else
{
Console.WriteLine("為 null");
}
模式匹配:C# 7.0 引入了模式匹配,允許在 is 表達式中進行類型檢查和轉(zhuǎn)換:
object obj = "追逐時光者";
if (obj is string str)
{
Console.WriteLine($" {str}");
}
else
{
Console.WriteLine("不是指定類型");
}
列表模式:從 C# 11 開始,可以使用列表模式來匹配列表或數(shù)組的元素。以下代碼檢查數(shù)組中處于預(yù)期位置的整數(shù)值:
int[] empty = [];
int[] one = [1];
int[] odd = [1, 3, 5];
int[] even = [2, 4, 6];
int[] fib = [1, 1, 2, 3, 5];
Console.WriteLine(odd is [1, _, 2, ..]); // false
Console.WriteLine(fib is [1, _, 2, ..]); // true
Console.WriteLine(fib is [_, 1, 2, 3, ..]); // true
Console.WriteLine(fib is [.., 1, 2, 3, _ ]); // true
Console.WriteLine(even is [2, _, 6]); // true
Console.WriteLine(even is [2, .., 6]); // true
Console.WriteLine(odd is [.., 3, 5]); // true
Console.WriteLine(even is [.., 3, 5]); // false
Console.WriteLine(fib is [.., 3, 5]); // true
as 運算符
as 運算符嘗試將對象轉(zhuǎn)換為特定類型,如果轉(zhuǎn)換失敗,則返回 null 而不是拋出異常。它通常用于在不需要顯式檢查對象是否為特定類型的情況下進行安全的類型轉(zhuǎn)換。
object title = "Hello DotNetGuide";
string str = title as string;
if (str != null)
{
Console.WriteLine("是 string 類型: " + str);
}
else
{
Console.WriteLine("不是 string 類型");
}
int? num = title as int?;
if (num.HasValue)
{
Console.WriteLine("是 int 類型: " + num.Value);
}
else
{
Console.WriteLine("不是 int 類型");
}
null是什么類型?
null 關(guān)鍵字是表示不引用任何對象的空引用的文字值。 null是引用類型變量的默認(rèn)值。 普通值類型不能為 null,可為空的值類型除外。
new關(guān)鍵字的作用?
- 運算符:創(chuàng)建類型的新實例
- 修飾符:可以顯式隱藏從基類繼承的成員。
- 泛型約束:泛型約束定義,約束可使用的泛型類型。
return、continue、break的區(qū)別?
return:
結(jié)束整個方法,return關(guān)鍵字并不是專門用于跳出循環(huán)的,return的功能是結(jié)束一個方法。 一旦在循環(huán)體內(nèi)執(zhí)行到一個return語句,return語句將會結(jié)束該方法,循環(huán)自然也隨之結(jié)束。與continue和break不同的是,return直接結(jié)束整個方法,不管這個return處于多少層循環(huán)之內(nèi)。
continue:
結(jié)束本次循環(huán),然后持續(xù)進行下一次循環(huán)。
break:
break用于完全結(jié)束一個循環(huán),跳出循環(huán)體。不管是哪種循環(huán),一旦在循環(huán)體中遇到break,系統(tǒng)將完全結(jié)束循環(huán),開始執(zhí)行循環(huán)之后的代碼。
yield
yield關(guān)鍵字在C#中簡化了數(shù)據(jù)迭代的方式,實現(xiàn)了按需生成數(shù)據(jù),自動維護迭代狀態(tài),減少了內(nèi)存占用,并允許在迭代時執(zhí)行復(fù)雜邏輯。
咱們來看看傳統(tǒng)迭代方式和yield關(guān)鍵字迭代方式對比,是否如傳說中的代碼實現(xiàn)起來更簡潔和高效:
/// <summary>
/// 傳統(tǒng)迭代方式和yield關(guān)鍵字迭代方式對比
/// </summary>
public static void IteratorComparisonRun()
{
Console.WriteLine("迭代器方法使用yield關(guān)鍵字:");
foreach (var number in GetNumbersWithYield())
{
Console.WriteLine(number);
}
Console.WriteLine("傳統(tǒng)迭代方法返回一個List<int>");
var numbers = GetNumbersWithoutYield();
foreach (var number in numbers)
{
Console.WriteLine(number);
}
}
/// <summary>
/// 迭代器方法使用yield關(guān)鍵字
/// </summary>
/// <returns></returns>
public static IEnumerable<int> GetNumbersWithYield()
{
for (int i = 0; i < 6; i++)
{
yield return i;
}
}
/// <summary>
/// 傳統(tǒng)迭代方法返回一個List<int>
/// </summary>
/// <returns></returns>
public static List<int> GetNumbersWithoutYield()
{
var numbers = new List<int>();
for (int i = 0; i < 6; i++)
{
numbers.Add(i);
}
return numbers;
}
圖片
什么情況不能使用yield關(guān)鍵字
- 帶有 in、ref 或 out 參數(shù)的方法。
- Lambda 表達式和匿名方法。
- 在 C# 13 之前,yield 在具有 unsafe 塊的任何方法中都無效。從 C# 13 開始,可以在包含 unsafe 塊的方法中使用 yield,但不能在 unsafe 塊中使用。
- 不能在catch和finally塊中使用yield return和yield break。
- 不能在具有catch塊的try塊中使用yield return和yield break。
- 可以在只有finally塊的try塊中使用yield return和yield break。
params
params適用于參數(shù)個數(shù)動態(tài)變化的場景,例如日志、數(shù)學(xué)計算或格式化輸出等,減少冗余代碼,增強方法通用性。
在 C# 13 之前:
params 僅支持一維數(shù)組(如params int[] list、params object[] list)。調(diào)用方法時需顯式傳遞數(shù)組或數(shù)組元素類型的參數(shù)的逗號分隔列表。
在 C# 13 中:
params 修飾符并不局限于數(shù)組類型。 現(xiàn)在可以將 params 用于任何已識別的集合類型,包括 System.Span<T>、System.ReadOnlySpan<T>,以及那些實現(xiàn) System.Collections.Generic.IEnumerable<T> 并具有 Add 方法的類型。 除了具體類型外,還可以使用接口 System.Collections.Generic.IEnumerable<T>、System.Collections.Generic.IReadOnlyCollection<T>、System.Collections.Generic.IReadOnlyList<T>、System.Collections.Generic.ICollection<T>和 System.Collections.Generic.IList<T>。
注意事項
在方法聲明中的 params 關(guān)鍵字之后不允許有任何其他參數(shù),并且在方法聲明中只允許有一個 params 關(guān)鍵字。
goto跳轉(zhuǎn)語句
- goto 語句由關(guān)鍵字 goto 后跟一個標(biāo)簽名稱組成,通過標(biāo)簽名稱指定跳轉(zhuǎn)的位置。
- 可以在方法的任何地方放置標(biāo)簽,并且可以多次使用相同的標(biāo)簽。
圖片
/// <summary>
/// 使用goto進行代碼重試示例
/// </summary>
public static void GotoRetryUseExample()
{
int retryCount = 0;
for (int i = 0; i < 10; i++)
{
retryLogic:
try
{
//模擬可能出錯的操作
Random random = new Random();
int result = random.Next(0, 2);
if (result == 0)
{
throw new Exception("Error occurred");
}
Console.WriteLine("Operation successful on attempt: " + retryCount);
}
catch (Exception ex)
{
retryCount++;
if (retryCount < 3)
{
Console.WriteLine("Error occurred, retrying...");
goto retryLogic; //跳轉(zhuǎn)到重試邏輯
}
else
{
Console.WriteLine("Max retry limit reached.");
return;
}
}
}
}
參考文章
- https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/access-modifiers
- https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords