《前后端面試題
》專欄集合了前后端各個知識模塊的面試題,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。
文章目錄
- 一、本文面試題目錄
- 1. 簡述C#與.NET的關系
- 2. C#的值類型和引用類型有什么區別?各包含哪些常見類型?
- 3. 什么是裝箱和拆箱?會帶來什么性能影響?
- 4. string和String、int和Int32有什么區別?
- 5. 簡述C#中的常量(const)和只讀(readonly)的區別
- 6. 什么是 nullable 類型?如何使用?
- 7. ref和out關鍵字的作用及區別
- 8. params關鍵字的用法
- 9. C#中的訪問修飾符有哪些?各自的作用范圍是什么?
- 10. 簡述static關鍵字的用法(靜態類、靜態方法、靜態變量)
- 二、120道C#面試題目錄列表
一、本文面試題目錄
1. 簡述C#與.NET的關系
C#是一種編程語言,而.NET是一個跨平臺的開發框架,兩者緊密關聯但本質不同:
- C#:由微軟開發的面向對象編程語言,語法簡潔、類型安全,支持多種編程范式(面向對象、泛型、函數式等)。
- .NET:提供了運行時環境(CLR)、類庫(FCL)和開發工具的框架,支持多種編程語言(C#、VB.NET、F#等)。
關系說明:
- C#是.NET生態中最主流的編程語言
- C#代碼需編譯為中間語言(IL),再由.NET的CLR(公共語言運行時)執行
- .NET為C#提供基礎類庫、內存管理、異常處理等核心功能
簡單來說:C#是"工具",.NET是"工作臺",C#依托.NET框架實現各種功能。
2. C#的值類型和引用類型有什么區別?各包含哪些常見類型?
核心區別:存儲位置和內存管理方式不同
特性 | 值類型 | 引用類型 |
---|---|---|
存儲位置 | 棧(Stack) | 堆(Heap) |
內存分配 | 編譯時確定 | 運行時動態分配 |
賦值方式 | 復制值本身 | 復制引用地址 |
內存釋放 | 超出作用域自動釋放 | 由GC(垃圾回收)管理 |
常見類型:
-
值類型:
- 基本數據類型:int、float、double、bool、char
- 結構體:struct、enum
- 其他:decimal、DateTime
-
引用類型:
- 類:class
- 接口:interface
- 數組:Array
- 字符串:string(特殊引用類型,具有不可變性)
- 委托:delegate
示例代碼:
// 值類型示例
int a = 10;
int b = a; // 復制值
b = 20;
Console.WriteLine(a); // 輸出 10(a不受b影響)// 引用類型示例
class MyClass { public int Value; }
MyClass x = new MyClass { Value = 10 };
MyClass y = x; // 復制引用
y.Value = 20;
Console.WriteLine(x.Value); // 輸出 20(x和y指向同一對象)
3. 什么是裝箱和拆箱?會帶來什么性能影響?
裝箱(Boxing):將值類型轉換為引用類型的過程
- 原理:在堆上分配內存,復制值類型的值到新分配的內存,返回引用
拆箱(Unboxing):將引用類型(已裝箱的值類型)轉換回值類型的過程
- 原理:檢查對象是否為指定值類型的裝箱形式,將堆中的值復制到棧上
示例代碼:
int i = 10; // 值類型(棧上)
object obj = i; // 裝箱:int -> object(堆上分配內存)
int j = (int)obj; // 拆箱:object -> int(驗證類型并復制值)
性能影響:
- 裝箱會導致堆內存分配和復制操作,比普通賦值慢約10-20倍
- 拆箱需要類型檢查和值復制,也會消耗額外性能
- 頻繁的裝箱拆箱會增加GC壓力,導致性能下降
優化建議:
- 使用泛型(如List)避免裝箱
- 盡量在編譯時確定類型,減少運行時類型轉換
- 避免在循環等高頻操作中進行裝箱拆箱
4. string和String、int和Int32有什么區別?
本質區別:別名與實際類型的關系
-
int vs Int32:
- int是System.Int32的別名(C#關鍵字)
- 兩者完全等價,編譯后生成的IL代碼相同
- 其他類似:long對應Int64,short對應Int16等
-
string vs String:
- string是System.String的別名(C#關鍵字)
- 功能完全一致,但使用場景略有不同:
- 聲明字符串變量時推薦用string(語言關鍵字)
- 調用靜態方法時推薦用String(類名)
示例代碼:
// int與Int32等價
int a = 10;
Int32 b = 20;
int c = (Int32)30; // 類型轉換完全兼容// string與String等價
string s1 = "hello";
String s2 = "world";
bool isEqual = string.Equals(s1, s2); // 推薦用string調用實例方法
bool isNullOrEmpty = String.IsNullOrEmpty(s1); // 推薦用String調用靜態方法
最佳實踐:
- 保持代碼風格一致,避免混合使用
- 對于值類型,優先使用關鍵字形式(int、bool等)
- 對于字符串操作,實例方法用string,靜態方法用String
5. 簡述C#中的常量(const)和只讀(readonly)的區別
const(常量):編譯時確定值的常量,聲明后不可更改
- 必須在聲明時初始化
- 只能用于值類型和string
- 屬于編譯時常量,編譯器會直接替換其值
readonly(只讀):運行時確定值的常量,只能在構造函數中修改
- 可以在聲明時或構造函數中初始化
- 可用于任何類型
- 屬于運行時常量,保留變量特性
示例代碼:
public class MyClass
{// 常量:編譯時確定值public const int ConstValue = 100;// 只讀字段:可在聲明時初始化public readonly int ReadOnlyValue1 = 200;// 只讀字段:可在構造函數中初始化public readonly int ReadOnlyValue2;public MyClass(){ReadOnlyValue2 = 300; // 合法// ConstValue = 400; // 錯誤:const不能在構造函數中賦值}public void MyMethod(){// ReadOnlyValue1 = 500; // 錯誤:只讀字段不能在方法中修改}
}
主要區別表格:
特性 | const | readonly |
---|---|---|
初始化時機 | 聲明時必須初始化 | 聲明時或構造函數中 |
可修改性 | 完全不可修改 | 僅能在構造函數中修改 |
適用類型 | 僅值類型和string | 所有類型 |
編譯處理 | 編譯器直接替換值 | 保留變量引用 |
靜態特性 | 隱式靜態 | 可靜態可實例 |
6. 什么是 nullable 類型?如何使用?
nullable類型:允許值類型(如int、bool等)接受null值的特殊類型
- 解決了值類型默認不能為null的限制
- 語法:在值類型后加
?
,如int?
等價于Nullable<int>
使用場景:
- 數據庫交互(表示可空字段)
- 需區分"未賦值"和"默認值"的場景
- 可選參數或可選值
示例代碼:
// 聲明可空類型
int? nullableInt = null;
bool? nullableBool = true;
DateTime? nullableDate = new DateTime(2023, 1, 1);// 賦值操作
nullableInt = 100; // 可以賦值為正常值
nullableInt = null; // 也可以賦值為null// 檢查是否有值
if (nullableInt.HasValue)
{int value = nullableInt.Value; // 獲取值Console.WriteLine($"值為: {value}");
}
else
{Console.WriteLine("值為null");
}// 空合并運算符(??)
int result = nullableInt ?? -1; // 如果為null則使用-1
Console.WriteLine(result); // 輸出 -1(因為nullableInt當前為null)// 條件運算符(?.)
int? length = nullableDate?.Day; // 安全訪問,避免NullReferenceException
注意事項:
- 引用類型本身可null,無需使用nullable類型
- 可空類型之間可以相互轉換
- 使用
GetValueOrDefault()
可獲取值或默認值(不拋異常)
7. ref和out關鍵字的作用及區別
ref關鍵字:用于傳遞變量的引用,允許方法修改調用者的變量值
- 要求變量在傳遞前必須初始化
- 用于"傳遞并可能修改"已有變量
out關鍵字:用于傳遞變量的引用,強調方法必須為變量賦值
- 變量在傳遞前可以不初始化
- 用于"輸出"多個返回值的場景
示例代碼:
// ref關鍵字示例
int x = 10;
ModifyWithRef(ref x);
Console.WriteLine(x); // 輸出 20void ModifyWithRef(ref int value)
{value *= 2; // 修改原始變量的值
}// out關鍵字示例
int y; // 可以不初始化
bool success = TryParseNumber("123", out y);
if (success)
{Console.WriteLine(y); // 輸出 123
}bool TryParseNumber(string input, out int result)
{// 方法必須為out參數賦值if (int.TryParse(input, out result)){return true;}result = 0; // 即使失敗也必須賦值return false;
}
主要區別:
特性 | ref | out |
---|---|---|
初始化要求 | 傳遞前必須初始化 | 傳遞前可未初始化 |
賦值要求 | 方法內可賦可不賦 | 方法內必須賦值 |
主要用途 | 傳遞并修改已有值 | 返回多個結果值 |
數據流向 | 雙向(進和出) | 單向(僅出) |
使用建議:
- 需要修改已有變量時用ref
- 需要返回多個結果時用out(如TryXXX模式)
- C# 7.0+支持out變量聲明簡化:
TryParseNumber("123", out int y)
8. params關鍵字的用法
params關鍵字:允許方法接受數量可變的參數,這些參數被視為數組處理
- 簡化了調用方傳遞多個參數的語法
- 每個方法只能有一個params參數,且必須是最后一個參數
使用場景:
- 不確定參數數量的方法(如字符串格式化、數學計算)
- 希望提供更簡潔調用方式的API
示例代碼:
// 定義帶params參數的方法
public static int Sum(params int[] numbers)
{int total = 0;foreach (int num in numbers){total += num;}return total;
}// 調用方式
public static void Main()
{// 1. 傳遞多個參數int sum1 = Sum(1, 2, 3, 4);Console.WriteLine(sum1); // 輸出 10// 2. 傳遞數組int[] values = { 5, 6, 7 };int sum2 = Sum(values);Console.WriteLine(sum2); // 輸出 18// 3. 傳遞零個參數int sum3 = Sum();Console.WriteLine(sum3); // 輸出 0
}// 結合其他參數使用(params必須是最后一個)
public static string FormatMessage(string format, params object[] args)
{return string.Format(format, args);
}// 調用
string message = FormatMessage("Name: {0}, Age: {1}", "Alice", 30);
注意事項:
- params參數本質是數組,方法內部按數組處理
- 避免在高性能場景中過度使用(數組分配有性能開銷)
- 不能與ref或out同時使用
9. C#中的訪問修飾符有哪些?各自的作用范圍是什么?
C#提供5種訪問修飾符,用于控制類型和成員的訪問權限:
-
public(公共)
- 完全公開,任何地方都可訪問
- 適用:需要被外部廣泛訪問的成員
-
private(私有)
- 僅在當前類或結構體內部可訪問
- 適用:類內部的實現細節,默認訪問級別(接口和枚舉除外)
-
protected(保護)
- 在當前類及派生類中可訪問
- 適用:需要被繼承類使用的成員
-
internal(內部)
- 僅在當前程序集(項目)內可訪問
- 適用:同一程序集內共享的類型或成員
-
protected internal(保護內部)
- 在當前程序集內或派生類中可訪問(取protected和internal的并集)
- 適用:需要在程序集內或繼承體系中共享的成員
示例代碼:
public class MyBaseClass
{public int PublicField; // 任何地方可訪問private int PrivateField; // 僅MyBaseClass內部可訪問protected int ProtectedField; // MyBaseClass及派生類可訪問internal int InternalField; // 同一程序集內可訪問protected internal int ProtectedInternalField; // 同一程序集或派生類可訪問public void PublicMethod(){// 類內部可訪問所有成員PrivateField = 1;ProtectedField = 2;}
}public class MyDerivedClass : MyBaseClass
{public void AccessBaseMembers(){PublicField = 1; // 允許// PrivateField = 2; // 錯誤:無法訪問私有成員ProtectedField = 3; // 允許(派生類)// InternalField = 4; // 允許僅當在同一程序集ProtectedInternalField = 5; // 允許(派生類)}
}public class ExternalClass
{public void AccessMembers(MyBaseClass obj){obj.PublicField = 1; // 允許// obj.PrivateField = 2; // 錯誤// obj.ProtectedField = 3; // 錯誤// obj.InternalField = 4; // 允許僅當在同一程序集// obj.ProtectedInternalField = 5; // 允許僅當在同一程序集}
}
最佳實踐:
- 遵循"最小權限原則",盡量使用更嚴格的訪問級別
- 類的字段通常設為private,通過public屬性暴露
- 跨程序集共享的類型設為public,內部使用的設為internal
10. 簡述static關鍵字的用法(靜態類、靜態方法、靜態變量)
static關鍵字:用于聲明屬于類型本身而非實例的成員,主要有三種用法:
-
靜態變量(靜態字段)
- 屬于類本身,所有實例共享同一變量
- 內存中只存在一份,在第一次訪問類時初始化
- 常用于存儲全局狀態或計數器
-
靜態方法
- 屬于類本身,無需創建實例即可調用
- 只能訪問靜態成員,不能訪問實例成員
- 常用于工具類方法、工廠方法等
-
靜態類
- 只包含靜態成員,不能實例化
- 不能被繼承
- 常用于工具類(如Math、Convert)
示例代碼:
// 靜態類示例
public static class MathUtility
{// 靜態常量public const double Pi = 3.14159;// 靜態方法public static double CalculateArea(double radius){return Pi * radius * radius;}
}// 包含靜態成員的普通類
public class Counter
{// 靜態變量(所有實例共享)private static int _instanceCount = 0;// 實例變量(每個實例獨有)private int _id;// 靜態構造函數(初始化靜態成員)static Counter(){Console.WriteLine("靜態構造函數調用");}// 實例構造函數public Counter(){_instanceCount++;_id = _instanceCount;}// 靜態方法public static int GetTotalCount(){return _instanceCount;}// 實例方法public int GetId(){return _id;}
}// 使用示例
public static void Main()
{// 調用靜態類方法double area = MathUtility.CalculateArea(5);// 使用包含靜態成員的類Counter c1 = new Counter();Counter c2 = new Counter();Console.WriteLine(Counter.GetTotalCount()); // 輸出 2Console.WriteLine(c1.GetId()); // 輸出 1Console.WriteLine(c2.GetId()); // 輸出 2
}
靜態構造函數特點:
- 無參數,不能有訪問修飾符
- 自動調用,僅調用一次
- 用于初始化靜態成員
注意事項:
- 靜態成員生命周期與應用程序相同,可能導致內存占用問題
- 過度使用靜態成員會增加代碼耦合度,不利于測試
- 靜態方法不能被重寫,但可以被隱藏(使用new關鍵字)
二、120道C#面試題目錄列表
文章序號 | C#面試題120道 |
---|---|
1 | C#面試題及詳細答案120道(01-10) |
2 | C#面試題及詳細答案120道(11-20) |
3 | C#面試題及詳細答案120道(21-30) |
4 | C#面試題及詳細答案120道(31-40) |
5 | C#面試題及詳細答案120道(41-50) |
6 | C#面試題及詳細答案120道(51-60) |
7 | C#面試題及詳細答案120道(61-75) |
8 | C#面試題及詳細答案120道(76-85) |
9 | C#面試題及詳細答案120道(86-95) |
10 | C#面試題及詳細答案120道(96-105) |
11 | C#面試題及詳細答案120道(106-115) |
12 | C#面試題及詳細答案120道(116-120) |