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