文章目录
-
-
- [1. C# 中的委托(Delegate)是什么?事件(Event)是不是一种委托?](# 中的委托(Delegate)是什么?事件(Event)是不是一种委托?)
- [2. 简述 `private`、`protected`、`public`、`internal` 修饰符的访问权限。](#2. 简述
private、protected、public、internal修饰符的访问权限。) - [3. `override` 与方法重载(Overload)的区别。](#3.
override与方法重载(Overload)的区别。) - [4. 如果在一个 B/S 结构的系统中需要传递变量值,但是又不能使用 `Session`、`Cookie`、`Application`,您有几种方法进行处理?](#4. 如果在一个 B/S 结构的系统中需要传递变量值,但是又不能使用
Session、Cookie、Application,您有几种方法进行处理?) - [5. 请编程遍历页面上所有 `TextBox` 控件并给它赋值为 `string.Empty`?](#5. 请编程遍历页面上所有
TextBox控件并给它赋值为string.Empty?) - [6. 请编程实现一个冒泡排序算法。](#6. 请编程实现一个冒泡排序算法。)
- [7. 描述一下 C# 中索引器(Indexer)的实现过程,是否只能根据数字进行索引?](# 中索引器(Indexer)的实现过程,是否只能根据数字进行索引?)
- [8. 求以下表达式的值1-2+3-4+......+m,写出您想到的一种或几种实现方法。](#8. 求以下表达式的值1-2+3-4+……+m,写出您想到的一种或几种实现方法。)
- [9. 在下面的例子里,当使用 `new B()` 创建 B 的实例时,产生什么输出?](#9. 在下面的例子里,当使用
new B()创建 B 的实例时,产生什么输出?) - [10. `CTS`、`CLS`、`CLR` 分别作何解释?](#10.
CTS、CLS、CLR分别作何解释?) - [11. 什么是装箱(Boxing)和拆箱(Unboxing)?](#11. 什么是装箱(Boxing)和拆箱(Unboxing)?)
- [12. 什么是受管制的代码(Managed Code)?](#12. 什么是受管制的代码(Managed Code)?)
- [13. 什么是强类型系统?](#13. 什么是强类型系统?)
- [14. .NET 中读写数据库需要用到哪些类?它们的作用?](#14. .NET 中读写数据库需要用到哪些类?它们的作用?)
- [15. 列举 ASP.NET 页面之间传递值的几种方式。](#15. 列举 ASP.NET 页面之间传递值的几种方式。)
- [16. 什么是 Code-Behind 技术?](#16. 什么是 Code-Behind 技术?)
- [17. 在 .NET 中,配件(Assembly)的意思是?](#17. 在 .NET 中,配件(Assembly)的意思是?)
- [18. 常用的调用 `WebService` 的方法有哪些?](#18. 常用的调用
WebService的方法有哪些?) - [19. .NET Remoting 的工作原理是什么?](#19. .NET Remoting 的工作原理是什么?)
- [20. 在 C#中,`string str = null` 与 `string str = ""` 请尽量使用文字或图象说明其中的区别。](#20. 在 C#中,
string str = null与string str = ""请尽量使用文字或图象说明其中的区别。) - [21. 请详述在 dotnet 中类(`class`)与结构(`struct`)的异同?](#21. 请详述在 dotnet 中类(
class)与结构(struct)的异同?) - [22. 分析以下代码,完成填空。](#22. 分析以下代码,完成填空。)
- [23. SQL SERVER 服务器中,给定表 `table1` 中有两个字段 ID、LastUpdateDate,ID 表示更新的事务号,LastUpdateDate 表示更新时的服务器时间,请使用一句 SQL 语句获得最后更新的事务号。](#23. SQL SERVER 服务器中,给定表
table1中有两个字段 ID、LastUpdateDate,ID 表示更新的事务号,LastUpdateDate 表示更新时的服务器时间,请使用一句 SQL 语句获得最后更新的事务号。) - [24. 简要谈一下您对微软 .NET 构架下 Remoting 和 WebService 两项技术的理解以及实际中的应用。](#24. 简要谈一下您对微软 .NET 构架下 Remoting 和 WebService 两项技术的理解以及实际中的应用。)
- [25. 公司要求开发一个继承 `System.Windows.Forms.ListView` 类的组件,要求达到以下特殊功能:点击 `ListView` 各列列头时,能按照点击列的每行值进行重排视图中的所有行 (排序的方式如 DataGrid 相似)。根据您的知识,请简要谈一下您的思路。](#25. 公司要求开发一个继承
System.Windows.Forms.ListView类的组件,要求达到以下特殊功能:点击ListView各列列头时,能按照点击列的每行值进行重排视图中的所有行 (排序的方式如 DataGrid 相似)。根据您的知识,请简要谈一下您的思路。) - [26. 写出一条 Sql 语句:取出表 A 中第 31 到第 40 条记录(SQL Server,以自动增长的 ID 作为主键,注意:ID 可能不是连续的)。](#26. 写出一条 Sql 语句:取出表 A 中第 31 到第 40 条记录(SQL Server,以自动增长的 ID 作为主键,注意:ID 可能不是连续的)。)
- [27. 面向对象的语言具有哪三种基本特性?](#27. 面向对象的语言具有哪三种基本特性?)
- [28. 能用 `foreach` 遍历访问的对象需要实现哪个接口或声明哪个方法?](#28. 能用
foreach遍历访问的对象需要实现哪个接口或声明哪个方法?) - [29. GC 是什么?为什么要有 GC?](#29. GC 是什么?为什么要有 GC?)
- [30. `String s = new String("xyz");` 创建了几个 `String` Object?](#30.
String s = new String("xyz");创建了几个StringObject?) - [31. 启动一个线程是用 `run()` 还是 `start()`?](#31. 启动一个线程是用
run()还是start()?) - [32. 接口是否可继承接口? 抽象类是否可实现(`implements`)接口? 抽象类是否可继承实体类(`concrete class`)?](#32. 接口是否可继承接口? 抽象类是否可实现(
implements)接口? 抽象类是否可继承实体类(concrete class)?) - [33. 构造器(Constructor)是否可被 `override`?](#33. 构造器(Constructor)是否可被
override?) - [34. 是否可以继承 `String` 类?](#34. 是否可以继承
String类?) - [35. `try{}` 里有一个 `return` 语句,那么紧跟在这个 `try` 后的 `finally {}` 里的代码会不会被执行,什么时候被执行,在 `return` 前还是后?](#35.
try{}里有一个return语句,那么紧跟在这个try后的finally {}里的代码会不会被执行,什么时候被执行,在return前还是后?) - [36. 两个对象值相同(`x.Equals(y) == true`),但却可有不同的 `hash code`,这句话对不对?](#36. 两个对象值相同(
x.Equals(y) == true),但却可有不同的hash code,这句话对不对?) - [37. `switch` 是否能作用在 `byte` 上,是否能作用在 `long` 上,是否能作用在 `string` 上?](#37.
switch是否能作用在byte上,是否能作用在long上,是否能作用在string上?) - [38. 当一个线程进入一个对象的一个 `synchronized` 方法后,其它线程是否可进入此对象的其它方法?](#38. 当一个线程进入一个对象的一个
synchronized方法后,其它线程是否可进入此对象的其它方法?) - [39. `abstract` 的 `method` 是否可同时是 `static`,是否可同时是 `native`,是否可同时是 `synchronized`?](#39.
abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?) - [40. `List`, `Set`, `Map` 是否继承自 `Collection` 接口?](#40.
List,Set,Map是否继承自Collection接口?) - [41. `Set` 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用 `==` 还是 `Equals()`? 它们有何区别?](#41.
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是Equals()? 它们有何区别?) - [42. 数组有没有 `length()` 这个方法? `String` 有没有 `length()` 这个方法?](#42. 数组有没有
length()这个方法?String有没有length()这个方法?) - [43. `sleep()` 和 `wait()` 有什么区别?](#43.
sleep()和wait()有什么区别?) - [44. `short s1 = 1; s1 = s1 + 1;` 有什么错? `short s1 = 1; s1 += 1;` 有什么错?](#44.
short s1 = 1; s1 = s1 + 1;有什么错?short s1 = 1; s1 += 1;有什么错?) - [45. 谈谈 `final`, `finally`, `finalize` 的区别。](#45. 谈谈
final,finally,finalize的区别。) - [46. 如何处理几十万条并发数据?](#46. 如何处理几十万条并发数据?)
- [47. `Session` 有什么重大 BUG,微软提出了什么方法加以解决?](#47.
Session有什么重大 BUG,微软提出了什么方法加以解决?) - [48. 进程和线程的区别?](#48. 进程和线程的区别?)
- [49. 堆(Heap)和栈(Stack)的区别?](#49. 堆(Heap)和栈(Stack)的区别?)
- [50. 成员变量和成员函数前加 `static` 的作用?](#50. 成员变量和成员函数前加
static的作用?) - [51. ASP.NET 与 ASP 相比,主要有哪些进步?](#51. ASP.NET 与 ASP 相比,主要有哪些进步?)
- [52. 请说明在 .NET 中常用的几种页面间传递参数的方法,并说出他们的优缺点。](#52. 请说明在 .NET 中常用的几种页面间传递参数的方法,并说出他们的优缺点。)
- [53. 请指出 GAC 的含义?](#53. 请指出 GAC 的含义?)
- [54. 向服务器发送请求有几种方式?](#54. 向服务器发送请求有几种方式?)
- [55. `DataReader` 与 `DataSet` 有什么区别?](#55.
DataReader与DataSet有什么区别?) - [56. 软件开发过程一般有几个阶段?每个阶段的作用](#56. 软件开发过程一般有几个阶段?每个阶段的作用)
- [57. 在 C# 中 `using` 和 `new` 这两个关键字有什么意义,请写出你所知道的意义?](# 中
using和new这两个关键字有什么意义,请写出你所知道的意义?) - [`using` 关键字:](#
using关键字:) - [`new` 关键字:](#
new关键字:) - [58. 需要实现对一个字符串的处理,首先将该字符串首尾的空格去掉,如果字符串中间还有连续空格的话,仅保留一个空格,即允许字符串中间有多个空格,但连续的空格数不可超过一个。](#58. 需要实现对一个字符串的处理,首先将该字符串首尾的空格去掉,如果字符串中间还有连续空格的话,仅保留一个空格,即允许字符串中间有多个空格,但连续的空格数不可超过一个。)
- [59. 什么叫做 SQL 注入,如何防止?请举例说明。](#59. 什么叫做 SQL 注入,如何防止?请举例说明。)
- [60. 什么是反射(Reflection)?](#60. 什么是反射(Reflection)?)
- [61. 用 Singleton(单例)如何写设计模式?](#61. 用 Singleton(单例)如何写设计模式?)
- [62. 什么是 Application Pool?](#62. 什么是 Application Pool?)
- [63. 什么是虚函数(`virtual`)?什么是抽象函数(`abstract`)?](#63. 什么是虚函数(
virtual)?什么是抽象函数(abstract)?) - [64. 什么是 XML?](#64. 什么是 XML?)
- [65. 什么是 WebService?UDDI?](#65. 什么是 WebService?UDDI?)
- [66. C# 里面对一个数组 `int[] myIntArray = {1, 2, 3, 4, 5};` 采用什么办法实现"去 3 留 4"?](# 里面对一个数组
int[] myIntArray = {1, 2, 3, 4, 5};采用什么办法实现“去 3 留 4”?) - [67. 在 .NET 中,`try...catch...finally` 中,如果 `catch` 块中出现了异常,`finally` 块还会执行吗?](#67. 在 .NET 中,
try...catch...finally中,如果catch块中出现了异常,finally块还会执行吗?) - [68. 什么是强命名(Strong Name)?它解决了什么问题?](#68. 什么是强命名(Strong Name)?它解决了什么问题?)
- [69. 什么是 `Code Access Security` (CAS)?](#69. 什么是
Code Access Security(CAS)?) - [70. 什么是 WebControl 和 UserControl?各有什么特点?](#70. 什么是 WebControl 和 UserControl?各有什么特点?)
- [71. C# 中的 `is` 和 `as` 操作符有什么区别?](# 中的
is和as操作符有什么区别?) - [72. 什么是 `Code-Behind`?](#72. 什么是
Code-Behind?) - [73. 什么是 `ViewState`?它有什么优缺点?](#73. 什么是
ViewState?它有什么优缺点?) - [74. 写出 `using System.Threading.Thread;` 在 C# 中的主要作用。](# 中的主要作用。)
- [75. .NET Remoting 的生命周期是怎么管理的?](#75. .NET Remoting 的生命周期是怎么管理的?)
- [76. 什么是 `ADO.NET`?](#76. 什么是
ADO.NET?) - [77. 解释 `sealed` 关键字在 C# 中的用途。](# 中的用途。)
- [78. C# 中 `const` 和 `readonly` 有什么区别?](# 中
const和readonly有什么区别?) - [79. C# 异常处理中,`throw` 和 `throw ex` 有什么区别?](# 异常处理中,
throw和throw ex有什么区别?) - [80. C# 中 `try-catch-finally` 的作用,何时可以省略 `catch` 或 `finally`?](# 中
try-catch-finally的作用,何时可以省略catch或finally?) - [81. 在 C# 中,抽象类与接口的异同。](# 中,抽象类与接口的异同。)
- [82. C# 中 `public static const int A = 1;` 语句有没有错误?](# 中
public static const int A = 1;语句有没有错误?) - [83. C# 中 `goto` 语句的使用。](# 中
goto语句的使用。) - [84. `try { } catch (Exception ex) { }` 在实际项目中应该如何使用?](#84.
try { } catch (Exception ex) { }在实际项目中应该如何使用?) - [85. C# 中 `sealed class` 有什么特点?](# 中
sealed class有什么特点?) - [86. C# 中的值类型(Value Type)和引用类型(Reference Type)的区别?](# 中的值类型(Value Type)和引用类型(Reference Type)的区别?)
- [87. 解释 `HashTable` 和 `Dictionary` 的区别。](#87. 解释
HashTable和Dictionary的区别。) - [88. C# 中 `string` 和 `String Build` 的区别?](# 中
string和String Build的区别?) - [89. C# 中 `final`、`finally`、`finalize` 的区别?](# 中
final、finally、finalize的区别?) - [90. 为什么说 `string` 是引用类型但表现得像值类型?](#90. 为什么说
string是引用类型但表现得像值类型?) - [91. 什么是 `DAL`、`BLL`、`UIL`?](#91. 什么是
DAL、BLL、UIL?) - [92. C# 里面有多少种循环?](# 里面有多少种循环?)
- [93. 什么时候使用 `try/catch` 语句?](#93. 什么时候使用
try/catch语句?) - [94. 委托中 `+=` 和 `=` 的含义?](#94. 委托中
+=和=的含义?) - [95. C# 中 `ref` 和 `out` 关键字的区别?](# 中
ref和out关键字的区别?) - [96. C# 中的泛型(Generics)是什么?](# 中的泛型(Generics)是什么?)
- [97. 解释 `try-catch-finally` 的嵌套结构。](#97. 解释
try-catch-finally的嵌套结构。) - [98. C# 中 `IComparable` 和 `IComparer` 接口的作用和区别。](# 中
IComparable和IComparer接口的作用和区别。) - [99. 什么是 `Serialization`(序列化)?C# 中常用的序列化方式有哪些?](# 中常用的序列化方式有哪些?)
- [100. 简述 `LINQ` (Language Integrated Query) 的基本概念和优势。](#100. 简述
LINQ(Language Integrated Query) 的基本概念和优势。)
-
1. C# 中的委托(Delegate)是什么?事件(Event)是不是一种委托?
答:
- 委托 (Delegate)是一种类型安全的 函数指针,它定义了一个方法的签名,可以引用和调用任何匹配该签名的方法。
- 事件 (Event)是一种基于委托的特殊机制,用于实现对象间的通知。事件是对委托的封装,它限制了订阅者只能添加或移除处理程序,而不能直接触发(发布者除外)。
详细解释:
委托是 C# 实现回调和事件机制的基础,它允许方法像数据一样被传递。
- 语法示例:
public delegate int MyDelegate(string s);
2. 简述 private、protected、public、internal 修饰符的访问权限。
答:
| 修饰符 | 访问权限 | 适用范围 |
|---|---|---|
| public | 完全公开 | 任何代码,包括同一程序集和其他程序集。 |
| internal | 程序集内部 | 只有在 同一程序集 (Assembly) 内的代码可以访问。 |
| protected | 保护成员 | 只有在 类的内部 或 派生类 的内部才可以访问。 |
| private | 私有成员 | 只有在 定义该成员的类的内部 才可以访问。 |
详细解释:
internal权限常用于库的开发,它可以对外部程序集隐藏实现细节。protected internal(组合修饰符)允许在当前程序集内的任何地方,或在派生自包含类的其他程序集内的类中访问。
3. override 与方法重载(Overload)的区别。
答:
| 特性 | 重载(Overload) | 重写(Override) |
|---|---|---|
| 目的 | 适应不同参数需求,提供多种调用方式(编译时多态)。 | 改变继承自基类的行为,实现多态性(运行时多态)。 |
| 签名 | 名称相同 ,参数列表必须不同。 | 名称和参数列表必须与基类方法完全相同。 |
| 关键字 | 无特殊关键字。 | 派生类使用 override,基类方法必须为 virtual 或 abstract。 |
4. 如果在一个 B/S 结构的系统中需要传递变量值,但是又不能使用 Session、Cookie、Application,您有几种方法进行处理?
答:
除了不能使用的三种方法外,主要有以下几种方法:
QueryString(URL 参数): 通过 URL 传递参数,如Page2.aspx?id=123。Server.Transfer: 在服务器端进行页面跳转,将当前请求的上下文传递给新的页面,新页面可以访问前一个页面的控件和变量。ViewState: 存储在页面隐藏字段中的数据,用于在同一页面回发(Postback)之间维护控件状态,(严格来说是在页面内部传递)。HttpContext.Items: 存储在当前 HTTP 请求的生命周期内共享的数据集合。
详细解释:
Server.Transfer是最符合在不使用Session等情况下在服务器端页面之间传递数据的可靠方法。它避免了客户端的二次请求,且保留了原始请求的上下文。
5. 请编程遍历页面上所有 TextBox 控件并给它赋值为 string.Empty?
答:
假设是在 WinForms 或 ASP.NET WebForms 环境中,代码如下(使用 C# 模式匹配):
csharp
// 假设这是在一个容器控件(如 Form 或 Panel)的方法中
foreach (System.Windows.Forms.Control control in this.Controls)
{
// 使用 'is T variable' 模式匹配,避免额外的类型转换
if (control is System.Windows.Forms.TextBox tb)
{
tb.Text = string.Empty;
}
// 如果需要遍历嵌套控件,则需要递归调用
// else if (control.HasChildren)
// {
// // 递归调用处理嵌套容器
// ClearTextBoxes(control.Controls);
// }
}
6. 请编程实现一个冒泡排序算法。
答:
冒泡排序(Bubble Sort)通过重复地走访过要排序的数列,一次比较两个相邻元素,如果它们的顺序错误就交换它们,直到没有元素需要交换,即完成排序。
csharp
public static void BubbleSort(int[] array)
{
int n = array.Length;
int temp;
// n-1 轮排序
for (int i = 0; i < n - 1; i++)
{
// 每一轮将最大的元素"冒泡"到末尾
for (int j = 0; j < n - 1 - i; j++)
{
if (array[j] > array[j + 1])
{
// 交换 array[j] 和 array[j+1]
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
7. 描述一下 C# 中索引器(Indexer)的实现过程,是否只能根据数字进行索引?
答:
- 实现过程: 索引器允许类的实例像数组一样被索引。它通过定义一个名为
this的特殊属性来实现,并使用get和set访问器来存取数据。 - 索引类型: 不是,索引器不限于使用数字(
int)进行索引。 它可以接受任何有效的 C# 类型作为索引参数,例如string、Guid或自定义结构体。
详细解释:
- 语法示例:
public object this[string key] { get; set; } - 索引器常用于创建集合类或字典的封装,提供简洁的访问语法。
8. 求以下表达式的值1-2+3-4+......+m,写出您想到的一种或几种实现方法。
答:
表达式为 1-2+3-4+......+m
实现方法一:循环迭代法
csharp
public static int CalculateAlternatingSum(int m)
{
int sum = 0;
for (int i = 1; i <= m; i++)
{
sum += (i % 2 == 1) ? i : -i; // 奇数加,偶数减
}
return sum;
}
实现方法二:数学公式法(更高效)
csharp
public static int CalculateAlternatingSum_Math(int m)
{
if (m % 2 == 0)
{
return -m / 2;
}
else
{
return (m + 1) / 2;
}
}
9. 在下面的例子里,当使用 new B() 创建 B 的实例时,产生什么输出?
csharp
using System;
class A {
public A()
{
PrintFields();
}
public virtual void PrintFields(){}
}
class B:A
{
int x=1; int y;
public B()
{
y=-1;
}
public override void PrintFields()
{
Console.WriteLine("x={0},y={1}",x,y);
}
}
答:
输出结果为:
csharp
x=1,y=0
详细解释:
- 创建
B实例时,先调用基类A的构造函数A()。 - 在
A()中调用了virtual方法PrintFields(),根据运行时多态性,实际执行的是派生类B的override方法。 - 在执行
B.PrintFields()时:B的实例字段(x=1,y默认值0)已初始化。B的构造函数体 (y=-1;) 尚未执行。
- 因此输出
x=1,y=0。随后B的构造函数体执行,y变为1,但此时输出已完成。
10. CTS、CLS、CLR 分别作何解释?
答:
- CTS (Common Type System / 通用类型系统): 定义了 .NET 运行时使用的所有数据类型和编程构造的规范,确保跨语言兼容性。
- CLS (Common Language Specification / 通用语言规范): 是 CTS 的一个子集,定义了可供所有遵循 CLS 的 .NET 语言使用的公共特性和约束。
- CLR (Common Language Runtime / 公共语言运行库): 是 .NET 的核心运行时环境,负责执行托管代码,提供垃圾回收(GC)、异常处理、JIT 编译等服务。
11. 什么是装箱(Boxing)和拆箱(Unboxing)?
答:
- 装箱(Boxing): 将 值类型 (Value Type,如
int)隐式转换为 引用类型 (Reference Type,通常是object或接口)。此过程涉及在 堆 上分配内存和数据复制。 - 拆箱(Unboxing): 将 引用类型 (如
object)显式转换回 值类型。此过程涉及类型检查和从堆到栈的数据复制。
详细解释:
装箱和拆箱是资源密集型操作,应尽量避免在高性能代码中频繁使用。
12. 什么是受管制的代码(Managed Code)?
答:
- 受管制的代码 是指在 CLR (公共语言运行库) 环境下执行的代码。
- 特点: 它受益于 CLR 提供的各种服务,包括自动垃圾回收(GC)、异常处理、安全检查和类型安全。C# 代码默认都是托管代码。
13. 什么是强类型系统?
答:
- 强类型系统 是一种编程语言的特性,它要求所有变量和表达式都必须在编译时或运行时具有明确的类型,并且严格限制了不同类型之间的隐式转换。
- C# 属于强类型语言。 这种机制有助于在程序运行前捕获类型错误,增强代码的可靠性和稳定性。
14. .NET 中读写数据库需要用到哪些类?它们的作用?
答:
在 ADO.NET 中,常用的数据库操作类(以 SqlClient 为例)及其作用:
| 类/接口 | 作用 |
|---|---|
| SqlConnection | 建立和管理与数据库的连接。 |
| SqlCommand | 执行 SQL 语句或存储过程的命令对象。 |
| SqlDataReader | 提供高性能、只进、只读的流式数据读取方式。 |
| SqlDataAdapter | 在 DataSet 和数据库之间进行数据适配,用于填充和更新数据。 |
| DataSet | 内存中的数据缓存(虚拟数据库),可存储多个表和它们的关系。 |
15. 列举 ASP.NET 页面之间传递值的几种方式。
答:
QueryString: 通过 URL 传递(简单,但长度有限,不安全)。Session变量: 存储在服务器端,每个用户独立(简单,但可能因 IIS 回收或配置丢失)。Server.Transfer: 在服务器端重定向,保留HttpContext上下文。Cookie: 存储在客户端(简单,但可能被禁用或伪造)。Application变量: 存储在服务器端,所有用户共享(全局,但需要处理并发)。PostBackUrl和PreviousPage属性: 用于跨页面回发。ViewState: 主要用于同一页面回发,存储在隐藏字段中。
16. 什么是 Code-Behind 技术?
答:
- Code-Behind (代码后置)技术是 .NET WebForms 和 WPF 等平台中将 UI 界面标记 (如
.aspx或.xaml)与其 事件处理和业务逻辑代码 (如.cs文件)进行分离的技术。 - 它通过 分部类(Partial Class)机制,将标记语言编译成的类和代码文件中的类合并,提高了代码的可维护性和清晰度。
17. 在 .NET 中,配件(Assembly)的意思是?
答:
- 程序集 (Assembly,亦称配件)是 .NET 的 基本部署单元、版本控制单元和重用单元。
- 它是一个文件(通常是
.dll或.exe),其中包含 IL 中间语言代码 、元数据 (Metadata)、资源 和一个描述其内容和依赖关系的 程序集清单。
18. 常用的调用 WebService 的方法有哪些?
答:
- 添加 Web 引用(Add Web Reference): 在 Visual Studio 中,通过向项目添加 Web 服务引用,工具会生成客户端代理类,允许像调用本地方法一样调用 Web 服务。
- 使用命令行工具(如 WSDL.exe): 通过命令行工具生成代理类。
19. .NET Remoting 的工作原理是什么?
答:
- .NET Remoting 是一种用于在不同的 应用程序域 、进程 或 计算机 之间进行通信的机制,它允许对象像本地对象一样被远程调用。
- 工作原理: 服务器端对象通过 通道 (Channel,如 TCP 或 HTTP)注册。客户端通过 代理对象 (Proxy)与远程对象通信。客户端调用代理对象的方法时,代理将方法调用信息 序列化 ,通过通道发送到服务器端。服务器端 反序列化 信息,执行实际方法,然后将结果序列化并返回给客户端。
20. 在 C#中,string str = null 与 string str = "" 请尽量使用文字或图象说明其中的区别。
答:
string str = null:str变量是一个 空引用 。它没有指向内存中的任何string对象。尝试访问其方法(如str.Length)会抛出NullReferenceException。string str = "":str变量指向一个有效的string对象,该对象存储在 堆 上,且其内容是 空字符串(长度为 0)。可以安全地访问其方法。
| 属性 | string str = null | string str = "" |
|---|---|---|
| 引用 | 无引用 | 有效引用 |
| 内存 | 栈上有变量,堆上无对象 | 栈上有变量,堆上有空字符串对象 |
| 长度 | N/A(抛异常) | 0 |
21. 请详述在 dotnet 中类(class)与结构(struct)的异同?
答:
| 特性 | 类 (class) | 结构体 (struct) |
|---|---|---|
| 类型 | 引用类型(Reference Type) | 值类型(Value Type) |
| 内存分配 | 分配在 堆(Heap)上,需要垃圾回收(GC)。 | 通常分配在 栈(Stack)上(作为局部变量)。 |
| 继承 | 支持继承,可以继承类和实现接口。 | 不支持继承(只能实现接口)。 |
| 传递方式 | 按 引用 传递。 | 按 值 传递(复制整个结构)。 |
| 可否为 null | 可以为 null。 | 不可以为 null(除非使用 Nullable,如 int?)。 |
详细解释: struct 适用于小且轻量级的数据结构(通常小于 16 字节),以减少堆内存分配和 GC 的开销。
22. 分析以下代码,完成填空。
csharp
string strTmp = "abcdefg某某某";
int i = System.Text.Encoding.Default.GetBytes(strTmp).Length;
int j = strTmp.Length;
// 以上代码执行完后,i=13 j=10
详细解释:
23. SQL SERVER 服务器中,给定表 table1 中有两个字段 ID、LastUpdateDate,ID 表示更新的事务号,LastUpdateDate 表示更新时的服务器时间,请使用一句 SQL 语句获得最后更新的事务号。
答:
使用子查询找出最大的 LastUpdateDate,然后返回对应 ID:
csharp
SELECT ID
FROM table1
WHERE LastUpdateDate = (
SELECT MAX(LastUpdateDate)
FROM table1
);
详细解释:
该查询通过在子查询中计算出 LastUpdateDate 字段的最大值,然后在外层查询中匹配该最大值的记录,从而找到最后更新的事务号 ID。
24. 简要谈一下您对微软 .NET 构架下 Remoting 和 WebService 两项技术的理解以及实际中的应用。
答:
| 特性 | .NET Remoting | WebService (SOAP/WSDL) |
|---|---|---|
| 通信协议 | 可配置(TCP/IP、HTTP、IPC 等)。 | 基于标准的 Web 协议(HTTP/S)。 |
| 数据格式 | 默认使用 二进制 格式(效率高)。 | 使用 XML (SOAP 协议) 格式(跨平台性好)。 |
| 跨平台性 | 差,通常只在 .NET 平台间使用。 | 优,基于开放标准,实现异构平台间的通信。 |
| 应用场景 | 高性能 、同构(都是 .NET)的环境,如集群内部的组件通信。 | 跨平台 、穿透防火墙 的场景,如企业间的 B2B 集成。 |
25. 公司要求开发一个继承 System.Windows.Forms.ListView 类的组件,要求达到以下特殊功能:点击 ListView 各列列头时,能按照点击列的每行值进行重排视图中的所有行 (排序的方式如 DataGrid 相似)。根据您的知识,请简要谈一下您的思路。
答:
核心思路是利用 ListView 的 ListViewItemSorter 属性和 ColumnClick 事件。
- 事件处理: 监听
ListView的ColumnClick事件。 - 实现 IComparer: 创建一个自定义的比较器类(例如
ListViewColumnSorter),该类必须实现System.Collections.IComparer接口。 - 比较逻辑: 在
IComparer的Compare方法中,根据传入的列索引、排序方向(升序/降序)和待比较的两个ListViewItem,获取其子项文本,并使用String.Compare或适当的类型比较逻辑(如int.Parse或DateTime.Parse)进行比较。 - 应用排序器: 在
ColumnClick事件中,更新排序方向和列索引,并将自定义的比较器实例赋值给ListView.ListViewItemSorter属性,然后调用ListView.Sort()方法强制ListView重新排序。
26. 写出一条 Sql 语句:取出表 A 中第 31 到第 40 条记录(SQL Server,以自动增长的 ID 作为主键,注意:ID 可能不是连续的)。
答:
在 SQL Server 2012 及更高版本中,推荐使用 OFFSET-FETCH 子句:
SELECT * FROM A
ORDER BY ID ASC -- 必须指定 ORDER BY 子句
OFFSET 30 ROWS -- 跳过前 30 行 (31 - 1 = 30)
FETCH NEXT 10 ROWS ONLY; -- 取出接下来的 10 行
详细解释:
在旧版本的 SQL Server(2005-2008 R2)中,可能需要使用 ROW_NUMBER() 函数或嵌套的 SELECT TOP 语句来实现,但 OFFSET-FETCH 是标准且性能较好的分页方式。
27. 面向对象的语言具有哪三种基本特性?
答:
面向对象的三大基本特性是:
- 封装 (Encapsulation)
- 继承 (Inheritance)
- 多态 (Polymorphism)
详细解释:
- 封装: 将数据和方法捆绑到类中,并通过访问修饰符隐藏实现细节。
- 继承: 允许子类继承父类的属性和方法,实现代码重用。
- 多态: 允许不同类的对象对同一消息做出不同的响应(C# 主要通过重载和重写实现)。
28. 能用 foreach 遍历访问的对象需要实现哪个接口或声明哪个方法?
答:
能用 foreach 遍历访问的对象需要:
- 实现
System.Collections.IEnumerable接口;或 - 声明一个公共的、无参数的
GetEnumerator()方法,该方法返回一个实现了System.Collections.IEnumerator接口的对象。
详细解释:
C# 编译器支持 迭代器模式 。如果一个类不实现 IEnumerable 但包含一个返回 IEnumerator 的 GetEnumerator() 方法,编译器也允许对其使用 foreach 循环。
29. GC 是什么?为什么要有 GC?
答:
- GC(Garbage Collector / 垃圾收集器): 是 .NET CLR 的一个核心组件,负责自动管理应用程序的内存。
- 为什么要有 GC:
- 避免内存泄漏: 程序员无需手动跟踪和释放不再使用的内存,防止程序长时间运行导致的内存耗尽。
- 提高开发效率: 减轻了程序员进行繁琐且易错的内存管理的负担,可以专注于业务逻辑。
- 提高安全性: 通过自动回收机制,GC 确保了类型安全和内存访问的有效性。
30. String s = new String("xyz"); 创建了几个 String Object?
答:
- 创建了两个
String对象:
- 一个对象是 字符串常量池 (String Intern Pool)中可能已存在的
"xyz"字符串字面量(如果不存在,则创建一个)。 - 另一个对象是通过
new String(...)表达式在 堆 上创建的新"xyz"实例。
详细解释:
使用 new 关键字会强制在堆上创建一个新的 string 实例,即使字符串内容在常量池中已经存在。变量 s 引用的是堆上的这个新对象。
31. 启动一个线程是用 run() 还是 start()?
答:
- 在 C# 中(对应 Java 中的问题),启动一个线程应该使用
Thread对象的Start()方法。
详细解释(C#):
- 在 C# 中,线程通常使用
System.Threading.Thread类。调用Thread.Start()方法后,CLR 才会为该线程分配资源,并调度它执行Thread构造函数中指定的方法。 - 如果直接调用线程的方法(相当于 Java 中的
run()),那么该方法会在当前线程上同步执行,而不会创建新的线程。
32. 接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?
答:
- 接口是否可继承接口? 可以。一个接口可以继承一个或多个其他接口。
- 抽象类是否可实现接口? 可以 。抽象类可以实现一个或多个接口,它可以实现接口的所有成员,也可以将部分或全部接口成员声明为
abstract,留给非抽象的派生类实现。 - 抽象类是否可继承实体类? 可以。抽象类可以继承非抽象的实体类(但实体类必须提供构造函数)。
33. 构造器(Constructor)是否可被 override?
答:
- 构造器不能被
override(重写)。
详细解释:
- 构造函数不属于类的继承成员,不能被标记为
virtual或override。 - 构造函数可以被 重载(Overload),即在同一个类中定义多个名称相同但参数列表不同的构造函数。
34. 是否可以继承 String 类?
答:
- 不可以。
详细解释:
- 在 C# 中,
System.String类是使用sealed(密封) 关键字修饰的。 sealed类不能被其他任何类继承,以保证类型安全和运行时优化。
35. try{} 里有一个 return 语句,那么紧跟在这个 try 后的 finally {} 里的代码会不会被执行,什么时候被执行,在 return 前还是后?
答:
finally块会执行。- 执行时间是在
try块的return语句将控制权返回给调用者之前 执行。
详细解释:
finally 块用于放置清理代码,保证无论 try 块中是否发生异常,或是否包含 return、break、continue,这部分代码都会被可靠地执行。
36. 两个对象值相同(x.Equals(y) == true),但却可有不同的 hash code,这句话对不对?
答:
- 不对。
详细解释:
- 哈希码协定(Hash Code Contract): 在 .NET 中,如果两个对象被认为相等(即
Equals()返回true),那么它们的GetHashCode()方法必须返回相同的哈希码。 - 原因: 如果
Equals为真但哈希码不同,将导致对象在基于哈希码的集合(如Dictionary或HashSet)中无法被正确查找。
37. switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 string 上?
答:
byte: 可以 。它可隐式转换为int。long: 不可以 。switch表达式必须是整数类型(如sbyte,byte,short,ushort,int,uint)、char、enum或string类型。string: 可以(自 C# 2.0 起)。
详细解释:
long 不支持直接用于 switch,因为它可能会导致大量的 case 值和性能问题。对于 long,通常使用 if-else if 链代替。
38. 当一个线程进入一个对象的一个 synchronized 方法后,其它线程是否可进入此对象的其它方法?
答:
- 不可以 (此问题基于 Java 或 C# 中的
lock关键字)。
详细解释(C#):
在 C# 中,当我们使用 lock(this) 或 lock(someObject) 时,锁住的是对象本身 。一旦一个线程获得了对象的锁,其他任何试图获取该对象锁的线程(无论是进入该对象的另一个 lock 块、还是另一个 synchronized 方法)都会被阻塞,直到第一个线程释放锁。因此,只要其他方法也使用了相同的锁对象,就不能进入。
39. abstract 的 method 是否可同时是 static,是否可同时是 native,是否可同时是 synchronized?
答:
- 都不能。
详细解释(C#):
abstract和static:abstract方法必须在派生类中被重写(override),但static方法属于类本身,不能被重写。两者是冲突的。abstract和synchronized:abstract方法没有方法体,而synchronized(C# 中的lock)是关于方法体执行时的同步机制。无体之法无需同步。native: 这是 Java 关键字,在 C# 中没有直接对应,但 C# 中的extern关键字表示方法由外部代码实现。abstract方法不能是extern,因为abstract要求在派生类中提供实现,而extern表示实现已经在外部。
40. List, Set, Map 是否继承自 Collection 接口?
答:
这是一个 Java 的概念,对应到 C# 的 集合接口 如下:
List: 对应 C# 的System.Collections.Generic.IList<T>,它继承自ICollection<T>。Set: 对应 C# 的System.Collections.Generic.ISet<T>,它继承自ICollection<T>。Map: 对应 C# 的System.Collections.Generic.IDictionary<TKey, TValue>,它 不直接继承自ICollection<T>,但它继承自IEnumerable接口,并且其内部的Keys和Values属性是ICollection类型的。
结论: 在 C# 中,List 和 Set 继承自 ICollection<T>,而 IDictionary 不继承。
41. Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用 == 还是 Equals()? 它们有何区别?
答:
- 区分重复:
Set(C# 中是HashSet<T>或SortedSet<T>)通过元素的 哈希码 (GetHashCode())和 相等性 (Equals())来区分重复。 Equals()vs==:==运算符: 默认行为是比较 引用相等性 (两个引用是否指向堆上的同一个对象)。对于值类型,它比较值。对于string类型,C# 默认重载为比较值相等性。Equals()方法:- 引用类型: 默认继承自
object,行为与==相同(比较引用)。但它经常被重写(override)来比较 值相等性 (例如string、自定义对象)。 - 值类型: 默认情况下,它比较字段的值相等性。
- 引用类型: 默认继承自
结论: Set 依赖于对象自定义的 GetHashCode() 和 重写的 Equals() 方法来确定两个对象在逻辑上是否重复。
42. 数组有没有 length() 这个方法? String 有没有 length() 这个方法?
答:
- 数组(Array): 没有
length()方法,它有一个Length属性 。- 示例:
int[] arr = new int[5]; int len = arr.Length;
- 示例:
String: 没有length()方法,它有一个Length属性 。- 示例:
string s = "test"; int len = s.Length;
- 示例:
详细解释:
这是 C# 的语法规范,方法名后面有括号,属性则没有。
43. sleep() 和 wait() 有什么区别?
答:
| 特性 | Thread.Sleep() (C#) | Monitor.Wait() (C#) |
|---|---|---|
| 释放锁 | 不释放 当前线程持有的任何锁。 | 释放 当前线程持有的对象锁(同步监视器)。 |
| 用途 | 暂停当前线程的执行,达到计时或让步的目的。 | 用于线程间的通信和协调,使线程进入等待状态,直到被 Pulse() 或 PulseAll() 唤醒。 |
| 唤醒 | 时间到了自动被 OS 调度器唤醒。 | 必须由其他线程调用 Monitor.Pulse 或 Monitor.PulseAll 唤醒。 |
| 位置 | 可以在任何地方调用。 | 必须在锁定(lock)了目标对象的同步块内部调用。 |
44. short s1 = 1; s1 = s1 + 1; 有什么错? short s1 = 1; s1 += 1; 有什么错?
答:
short s1 = 1; s1 = s1 + 1;:有错。- 错误原因: 在 C# 中,整数常量
1默认为int类型。s1 + 1的结果是int类型。将int类型赋值给short类型属于 隐式的大范围向小范围转换,C# 编译器不允许,因为它可能导致数据丢失。 - 正确修改: 必须进行显式类型转换:
s1 = (short)(s1 + 1);
- 错误原因: 在 C# 中,整数常量
short s1 = 1; s1 += 1;: 无错 (正确)。- 原因: 复合赋值运算符(如
+=,=,=等)在 C# 中会自动包含一个隐式的类型转换。编译器会自动生成代码s1 = (short)(s1 + 1);。
- 原因: 复合赋值运算符(如
45. 谈谈 final, finally, finalize 的区别。
答:
这是 Java 概念,对应到 C# 中的概念是 sealed/const/readonly,finally 和 Finalize() 方法。
| 概念 | 对应 C# 关键字/方法 | 作用 |
|---|---|---|
| final (Java) | sealed / const / readonly | 用于 限制:sealed 限制类不能被继承;const 限制值不能被修改(编译时常量);readonly 限制字段只能在构造函数中初始化。 |
| finally | finally 块 | 异常处理结构的一部分,用于放置 必须执行 的清理代码,无论是否发生异常。 |
| finalize (Java) | Finalize() 方法(通过析构函数 ~ClassName() 实现) | 由 GC 在回收对象内存之前调用,用于执行非托管资源(如文件句柄、数据库连接)的 最后清理操作。 |
详细解释(C#): C# 推荐使用 IDisposable 接口和 using 语句进行确定性资源清理,而不是依赖不确定的 Finalize() 方法。
46. 如何处理几十万条并发数据?
答:
处理高并发大量数据的系统问题通常需要一个综合解决方案,包括:
- 数据库层面:
- 使用存储过程/事务: 确保数据操作的原子性和一致性。
- 数据库连接池: 减少连接创建和释放的开销。
- 优化 SQL 语句和索引: 加快查询速度。
- 读写分离/分库分表: 分散数据库压力。
- 应用架构层面:
- 异步编程(Async/Await): 释放线程,提高服务器并发处理能力(I/O 密集型操作)。
- 消息队列(MQ): 将高并发的请求(如订单)削峰填谷,解耦系统,转同步处理为异步处理。
- 缓存(Redis/Memcached): 缓存热点数据,减少数据库访问。
- 负载均衡: 将请求分配到多台服务器上。
47. Session 有什么重大 BUG,微软提出了什么方法加以解决?
答:
- 重大 BUG/问题: 在 Web 服务器集群或使用 IIS 进程回收机制时,
Session可能会丢失(默认的InProc模式将 Session 存储在当前 IIS 工作进程的内存中)。 - 微软的解决方案: 提供了不同的 Session 状态模式 来保证 Session 的持久性和可靠性:
- State Server 模式: 将 Session 存储在一个独立的 Windows 服务(ASP.NET 状态服务)中。
- SQL Server 模式: 将 Session 存储在 SQL Server 数据库中。
详细解释:
使用 State Server 或 SQL Server 模式可以使 Session 在 IIS 进程重启或服务器集群中共享,但代价是序列化/反序列化和网络传输的开销,性能相比 InProc 模式会降低。
48. 进程和线程的区别?
答:
| 特性 | 进程 (Process) | 线程 (Thread) |
|---|---|---|
| 资源分配 | 资源分配 的基本单位。 | CPU 调度和分派 的基本单位。 |
| 独立性 | 拥有独立的内存空间、文件句柄等资源。 | 共享其所属进程的内存空间和大部分资源。 |
| 组成 | 一个进程至少包含一个线程(主线程)。 | 一个进程可以有多个线程。 |
| 通信 | 跨进程通信相对复杂(IPC)。 | 线程间通信相对简单(共享内存)。 |
49. 堆(Heap)和栈(Stack)的区别?
答:
| 特性 | 栈 (Stack) | 堆 (Heap) |
|---|---|---|
| 内存分配 | 由编译器自动分配和释放。 | 由程序员分配和释放(在 C# 中通过 new 关键字分配,由 GC 释放)。 |
| 存储内容 | 存储 值类型 的实例、方法参数、局部变量和 引用类型变量的引用地址。 | 存储 引用类型 的实际对象实例(数据)。 |
| 特点 | 存取速度快,但容量有限。 | 存取速度相对慢,但容量大,且内存可动态调整。 |
| 生命周期 | 随着方法执行的结束而自动销毁。 | 只有当没有引用指向它时,才会被 GC 回收。 |
50. 成员变量和成员函数前加 static 的作用?
答:
static 关键字用于声明属于 类型本身 而不是属于特定对象实例的成员。
static成员变量(类变量):- 作用: 存储该类的 所有实例共享 的数据。
- 特点: 无论创建多少个对象,
static变量在内存中只有一份副本。 - 用途: 统计实例数量、存储全局配置等。
static成员函数(类方法):- 作用: 执行与类本身相关的操作,不能访问任何实例成员(非静态字段和属性)。
- 特点: 只能通过类名直接调用,无需创建类的实例。
- 用途: 工具方法、工厂方法、单例模式的访问点等。
51. ASP.NET 与 ASP 相比,主要有哪些进步?
答:
ASP.NET 相较于传统的 ASP (Active Server Pages) 具有显著进步,主要体现在:
- 编译型 vs 解释型: ASP.NET 是 编译型 (代码先编译成 IL 存储在程序集中),性能远高于 ASP 的 解释型 脚本代码。
- 语言支持: ASP.NET 支持多种 CLS 兼容语言(C#, VB.NET),而 ASP 仅支持 VBScript 或 JScript。
- 代码分离: 支持 Code-Behind(代码后置)技术,将 UI 和业务逻辑分离,提高了代码的可维护性和可读性。
- 面向对象: ASP.NET 提供了强大的面向对象框架(如 Page 类、控件模型),ASP 则是基于过程和脚本的。
- 安全性: 内建了更强大的安全特性和配置。
52. 请说明在 .NET 中常用的几种页面间传递参数的方法,并说出他们的优缺点。
答:
| 方式 | 优点 | 缺点 |
|---|---|---|
| Session | 简单易用,服务器端存储,对用户不可见,容量大。 | 易丢失(IIS 回收或集群问题),占用服务器内存。 |
| QueryString | 简单,适用于 GET 请求,无需服务器资源。 | 不安全 (暴露在 URL 中),长度有限,只能传字符串。 |
| Cookie | 客户端存储,可持久化。 | 可能被禁用 或伪造,容量非常有限(约 4KB)。 |
| Server.Transfer | 服务器端跳转,保留 HttpContext,安全且快速。 | 只能在同一服务器 内跳转,地址栏 URL 不变。 |
| Application | 全局共享,所有用户可见。 | 需要处理并发问题,数据易被覆盖,服务器重启数据丢失。 |
| Hidden Field | 简单,随表单回发。 | 只能在同一页面 内传递,对用户可见(查看源码)。 |
53. 请指出 GAC 的含义?
答:
- GAC (Global Assembly Cache / 全局程序集缓存): 是 Windows 操作系统上的一个特殊目录,用于存储那些需要在多应用程序之间共享的 强命名程序集(Strong-Named Assemblies)。
详细解释:
- 强命名: 确保程序集的身份唯一性(通过名称、版本、公钥标记和文化信息)。
- 作用: 解决 DLL Hell 问题,实现程序集的并排(Side-by-Side)执行,允许多个版本的同一个程序集在系统中并存。
54. 向服务器发送请求有几种方式?
答:
主要有两种 HTTP 请求方式:
- GET
- 特点: 参数通过 URL(Query String)传递,数据量小,可见。常用于获取资源。
- POST
- 特点: 参数放在 HTTP 请求体中传递,数据量大,相对安全(不可见于 URL)。常用于提交/修改数据。
详细解释:
此外,现代 Web API/RESTful 服务还广泛使用 PUT (更新/替换)、DELETE(删除)等 HTTP 方法。在传统 WebForms 中,GET 常用于链接跳转,POST 常用于按钮提交。
55. DataReader 与 DataSet 有什么区别?
答:
| 特性 | DataReader | DataSet |
|---|---|---|
| 内存 | 流式、非缓存。直接从数据库读取。 | 内存缓存(虚拟数据库)。存储所有数据、表、关系。 |
| 连接 | 连接导向。读取数据时必须保持数据库连接处于打开状态。 | 断开式。一旦数据填充完毕,即可关闭数据库连接。 |
| 操作 | 只进、只读。一次只能访问一行数据。 | 可随机访问、筛选、排序、修改数据。 |
| 性能 | 高性能,读取速度快,内存消耗低。 | 性能相对较低,内存消耗高(需存储所有数据)。 |
详细解释: 简单地讲,DataReader 适用于快速、一次性读取大量数据的场景;DataSet 适用于需要离线操作、复杂关系或多次访问数据的场景。
56. 软件开发过程一般有几个阶段?每个阶段的作用
答:
软件开发生命周期(SDLC)通常包括以下主要阶段(以瀑布或迭代模型为基础):
- 需求分析(Requirement Analysis): 确定软件需要做什么,形成详细的需求文档。
- 架构设计(Architecture Design): 定义软件的整体结构、组件、接口和数据。分为高层设计和详细设计。
- 编码/实现(Implementation): 依据设计文档编写代码。
- 测试/质量保证(Testing/QA): 验证软件是否满足需求,发现并修复缺陷。
- 部署/发布(Deployment): 将软件安装、配置到生产环境。
- 维护(Maintenance): 软件发布后的修改、升级和支持。
57. 在 C# 中 using 和 new 这两个关键字有什么意义,请写出你所知道的意义?
答:
using 关键字:
using指令(引入命名空间): 用于导入命名空间,使得可以直接使用该命名空间下的类型,而无需完整限定。- 示例:
using System.Text;
- 示例:
using语句(非托管资源): 用于定义一个范围,在该范围结束时,会自动调用实现了IDisposable接口的对象的Dispose()方法,确保非托管资源 (如文件流、数据库连接)得到及时和确定的释放。- 示例:
using (var file = new FileStream(...)) { ... }
- 示例:
new 关键字:
- 创建实例: 用于调用构造函数,在堆上分配内存并初始化新的对象实例。
- 示例:
var obj = new MyClass();
- 示例:
- 隐藏基类成员: 在派生类中,使用
new修饰符来隐藏(Shadowing)基类中同名的非virtual成员(字段、方法、属性等)。- 示例:
public new void MyMethod() { ... }
- 示例:
- 类型约束: 在泛型定义中,
where T : new()约束泛型参数必须具有无参数构造函数。
58. 需要实现对一个字符串的处理,首先将该字符串首尾的空格去掉,如果字符串中间还有连续空格的话,仅保留一个空格,即允许字符串中间有多个空格,但连续的空格数不可超过一个。
答:
可以使用 C# 的 Trim() 方法去除首尾空格,然后使用 正则表达式 来替换字符串中间的连续空格。
csharp
using System.Text.RegularExpressions;
public static string NormalizeSpaces(string inputStr)
{
// 1. 去除首尾的空格
string trimmedStr = inputStr.Trim();
// 2. 使用正则表达式替换中间连续的空格
// "\s+" 匹配一个或多个连续的空白字符
// " " 表示替换为一个单一的空格
string result = Regex.Replace(trimmedStr, @"\s+", " ");
return result;
}
// 示例:NormalizeSpaces(" xx yy z ") 会返回 "xx yy z"
59. 什么叫做 SQL 注入,如何防止?请举例说明。
答:
- SQL 注入(SQL Injection): 是一种安全漏洞,攻击者通过在应用程序的输入字段中插入恶意的 SQL 代码,从而修改原始 SQL 语句的意图,以非授权方式访问、修改或删除数据库数据。
- 如何防止: 使用参数化查询(Parameterized Queries)。
举例说明:
| 类型 | 原始 SQL 语句 | 恶意输入 | 实际执行的 SQL |
|---|---|---|---|
| 易受攻击 | SELECT * FROM Users WHERE Name = ' $userInput ' | ' OR '1'='1 | SELECT * FROM Users WHERE Name = '' OR '1'='1' (绕过登录) |
| 防御后 | SELECT * FROM Users WHERE Name = @Name | ' OR '1'='1 | SELECT * FROM Users WHERE Name = '' OR '1'='1' (作为整体字符串值被查询) |
C# 防御示例:
csharp
// 防御:使用参数化查询
string sql = "SELECT * FROM Users WHERE UserName = @Name";
using (SqlCommand command = new SqlCommand(sql, connection))
{
// 将用户输入作为数据参数传递,而非SQL代码的一部分
command.Parameters.AddWithValue("@Name", userInput);
// ... 执行命令
}
60. 什么是反射(Reflection)?
答:
- 反射 是 .NET 的一种能力,允许程序在 运行时 动态地检查自身或其它程序集的 元数据,从而获取关于程序集、模块、类型(类、接口等)、成员(方法、属性、字段)的结构信息。
- 作用: 可以在运行时动态地创建对象实例、调用方法、访问属性和字段。
详细解释:
- 核心类型:
System.Type类是反射的入口点,可以用来获取任何类型的信息。 - 应用场景: 序列化/反序列化、单元测试框架、ORM 框架、插件系统。
61. 用 Singleton(单例)如何写设计模式?
答:
单例模式(Singleton)确保一个类只有一个实例,并提供一个全局访问点。在 C# 中,线程安全的懒汉式(延迟加载)实现如下:
csharp
public sealed class Singleton
{
// 使用 Lazy<T> 实现线程安全和延迟初始化
private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());
// 私有构造函数,阻止外部通过 new 创建实例
private Singleton()
{
// ... 初始化逻辑
}
// 全局访问点
public static Singleton Instance
{
get { return lazy.Value; }
}
}
详细解释:
使用 System.Lazy<T> 是 .NET Framework 4.0 之后实现线程安全单例的最佳实践。它保证了:
- 延迟加载: 只有在第一次访问
Instance.Value时才创建实例。 - 线程安全: 默认情况下,
Lazy<T>的初始化是线程安全的,确保多线程环境下只会创建一个实例。
62. 什么是 Application Pool?
答:
- Application Pool(应用程序池): 是 Internet Information Services (IIS) 中的一个配置容器,它将一个或多个 Web 应用程序相互隔离,并与 一组或多组工作进程(Worker Processes) 关联起来。
详细解释:
- 作用:
- 隔离性: 如果一个应用程序池中的某个 Web 应用崩溃,不会影响到其他应用程序池中运行的应用。
- 管理: 允许管理员独立地配置和管理每个应用程序池的设置(如内存限制、CPU 限制、进程回收策略等)。
63. 什么是虚函数(virtual)?什么是抽象函数(abstract)?
答:
| 特性 | 虚函数 (virtual) | 抽象函数 (abstract) |
|---|---|---|
| 实现体 | 有 实现体(提供默认行为)。 | 没有 实现体(只有签名,以分号结束)。 |
| 所属类 | 可以在 非抽象类 或 抽象类 中定义。 | 只能在 抽象类 (abstract class) 或 接口 (interface) 中定义。 |
| 派生类实现 | 派生类 可选 地使用 override 重写。 | 派生类 必须 使用 override 实现(除非派生类也是抽象类)。 |
| 目的 | 启用 运行时多态性,允许子类修改基类行为。 | 定义 契约,强制派生类提供特定功能的实现。 |
64. 什么是 XML?
答:
- XML (eXtensible Markup Language / 可扩展标记语言): 是一种用于存储和传输数据的标记语言。
- 特点:
- 自描述性: 标签名称可以由用户自定义,使其具有可读性。
- 结构化: 数据以树形结构组织,具有层次性。
- 平台无关: 广泛用于异构系统之间的数据交换。
详细解释: XML 不像 HTML 那样专注于显示数据,而是专注于携带数据。在 .NET 中,它常用于配置文件(如 app.config)、Web 服务数据交换(SOAP)和数据存储。
65. 什么是 WebService?UDDI?
答:
- WebService(Web 服务): 是一种基于网络的、分布式的模块化应用程序,它使用标准协议(如 HTTP、XML、SOAP)在异构系统之间进行通信。
- UDDI (Universal Description, Discovery and Integration / 通用描述、发现与集成): 是一套基于 Web 的、分布式的目录服务规范。它就像 Web 服务的"黄页",允许企业注册和查找 Web 服务。
详细解释:
在现代 .NET 开发中,RESTful API(通常基于 ASP.NET Core Web API)已经很大程度上取代了基于 SOAP/XML 的传统 WebService,但 WebService 仍存在于大量遗留系统中。
好的,我将继续为您解析剩余的 C#/.NET 面试问题。
66. C# 里面对一个数组 int[] myIntArray = {1, 2, 3, 4, 5}; 采用什么办法实现"去 3 留 4"?
答:
"去 3 留 4"的意思是删除数组中值为 3 的元素,同时保留值为 4 的元素。由于数组是固定长度的,实际操作是创建一个新集合或数组,将不符合删除条件的元素保留下来。
C# 实现方法(使用 LINQ):
csharp
int[] myIntArray = {1, 2, 3, 4, 5, 3, 4}; // 示例数组
// 使用 Where 方法过滤掉所有值为 3 的元素
var newArray = myIntArray.Where(x => x != 3).ToArray();
// newArray 结果为 {1, 2, 4, 5, 4}
详细解释(不使用 LINQ):
如果不能使用 LINQ,可以使用 List<int> 动态集合:
csharp
int[] myIntArray = {1, 2, 3, 4, 5};
List<int> tempList = new List<int>();
foreach (int num in myIntArray)
{
if (num != 3) // 只保留不等于 3 的元素
{
tempList.Add(num);
}
}
int[] newArray = tempList.ToArray();
67. 在 .NET 中,try...catch...finally 中,如果 catch 块中出现了异常,finally 块还会执行吗?
答:
finally块仍然会执行。
详细解释:
finally 块的唯一目的是确保其中的代码(通常是资源清理代码)无论 try 块中是否发生异常,以及 catch 块中是否发生异常,都会被执行。在 catch 块中抛出的异常,将跳过 finally 块之后的代码,但不会阻止 finally 块执行。
68. 什么是强命名(Strong Name)?它解决了什么问题?
答:
- 强命名 是一个由程序集标识(名称、版本、文化信息)、公钥 和 数字签名 组成的唯一标识符。
- 它解决了什么问题:
- 唯一性: 确保程序集在全球范围内的唯一性,防止命名冲突。
- 版本问题(DLL Hell): 允许同一程序集的不同版本并存和运行(Side-by-Side Execution)。
- 防篡改: 确保程序集自编译以来没有被第三方修改过。
详细解释:
只有具有强命名的程序集才能安装到 GAC(全局程序集缓存)中,供多个应用程序共享。
69. 什么是 Code Access Security (CAS)?
答:
- CAS (Code Access Security / 代码访问安全): 是 .NET Framework(已在 .NET Core/5+ 中弃用)用于限制 托管代码 执行权限的一种机制。
- 作用: 它根据代码的来源(例如,来自本地硬盘、Intranet、Internet)或数字签名,授予或拒绝代码访问受保护资源的权限。
- 原理: CAS 基于 证据 (Evidence,如代码来源 URL)和 权限集(Permission Set)来判断代码是否有权执行某些操作(如访问文件系统、数据库)。
70. 什么是 WebControl 和 UserControl?各有什么特点?
答:
这是一个 ASP.NET WebForms 的概念。
| 特性 | WebControl (自定义控件) | UserControl (用户控件) |
|---|---|---|
| 文件 | 编译后的 DLL 文件。 | .ascx 文件(包含标记和代码)。 |
| 继承 | 继承自 System.Web.UI.WebControls.WebControl 或 Control。 | 继承自 System.Web.UI.UserControl。 |
| 封装/重用 | 高封装性,可以跨多个项目重用,并安装到工具箱。 | 低封装性,只能在当前项目或应用程序内重用。 |
| 部署 | 部署为编译后的 DLL。 | 部署为源代码(.ascx)和编译后的代码文件。 |
| 开发难度 | 难度较高,需要手动处理状态管理和呈现逻辑。 | 难度较低,开发类似于普通页面。 |
71. C# 中的 is 和 as 操作符有什么区别?
答:
| 操作符 | 作用 | 结果 | 异常 |
|---|---|---|---|
| is | 类型检查。检查对象是否与给定类型兼容。 | 返回 bool (true 或 false)。 | 不抛出异常。 |
| as | 类型转换。尝试将对象转换为给定类型。 | 成功返回目标类型的对象,失败返回 null。 | 不抛出异常。 |
C# 示例:
csharp
object obj = "Hello";
// 使用 is (C# 7.0 模式匹配)
if (obj is string s)
{
// s 已经被安全地转换为 string 且不为 null
}
// 使用 as
string str = obj as string; // str 为 "Hello"
object nonStr = 123;
string nullStr = nonStr as string; // nullStr 为 null
72. 什么是 Code-Behind?
答:
- Code-Behind (代码后置)技术是指将 用户界面 的定义(如 HTML 标记、ASP.NET 控件定义)与处理 UI 事件的 程序逻辑 代码分离开来的技术。
- 实现方式: 在 ASP.NET WebForms 中,UI 放在
.aspx文件中,C# 代码放在.aspx.cs文件中。它们通过Partial Class(分部类)机制在编译时合并为一个完整的类。
73. 什么是 ViewState?它有什么优缺点?
答:
ViewState是 ASP.NET WebForms 用于在 同一页面 的两次回发(Postback)之间,自动维护页面和控件状态的机制。- 工作原理: 状态数据被序列化为一个 Base64 编码的字符串,存储在一个名为
__VIEWSTATE的隐藏字段中,随 HTTP 请求往返于客户端和服务器。 - 优点:
- 无需
Session或Cookie,服务器端无存储压力。 - 简单易用,自动维护控件状态。
- 无需
- 缺点:
- 增加带宽和延迟: 隐藏字段数据量大,增加了页面传输大小。
- 安全风险: 存储在客户端,可能被篡改(尽管默认情况下会进行 MAC 校验)。
74. 写出 using System.Threading.Thread; 在 C# 中的主要作用。
答:
- 作用: 导入
System.Threading命名空间,从而可以使用其中的类型,最主要的是Thread类。 Thread类作用: 允许开发者创建和控制新的执行线程,实现并发编程。- 主要方法/属性:
Start(): 启动线程。Sleep(): 暂停当前线程。Join(): 阻塞调用线程,直到该线程终止。IsBackground: 设置或获取线程是否为后台线程。
75. .NET Remoting 的生命周期是怎么管理的?
答:
.NET Remoting 中远程对象的生命周期由 租约(Leasing) 机制管理,而不是直接依赖 GC。
- 租约(Lease): 每个远程对象都有一个关联的租约,类似于对象的"生存时间"。
- 初始租约时间: 远程对象创建时有一个初始的租约时间(默认为 5 分钟)。
- 续约(Renewal): 当客户端调用远程对象的方法时,租约管理器会延长该对象的租约时间。
- 过期与回收: 如果租约时间到期且没有客户端续约,对象将被标记为回收。租约管理器会在一段时间后将对象放入 GC 队列等待回收。
76. 什么是 ADO.NET?
答:
- ADO.NET (ActiveX Data Objects .NET): 是 .NET Framework 中用于与数据源(如数据库)进行交互的一组类、接口和抽象。
- 特点: 采用断开式 的数据访问模型,但同时也支持传统的连接式(流式)访问。
- 核心组件:
DataReader(连接式)、DataSet(断开式)、Connection、Command、DataAdapter。
77. 解释 sealed 关键字在 C# 中的用途。
答:
sealed 关键字用于修饰 类 、方法 或 属性:
- 修饰类: 被
sealed修饰的类称为密封类 ,它不能被其他任何类继承。- 用途: 防止继承破坏类的设计,或出于安全考虑,以及实现某些性能优化。
- 修饰方法/属性: 在派生类中,用于修饰 重写(
override) 的方法或属性。一旦方法被sealed,则后续的派生类将不能再重写此方法。- 用途: 锁定继承链中某个点的行为实现。
78. C# 中 const 和 readonly 有什么区别?
答:
| 特性 | const (常量) | readonly (只读字段) |
|---|---|---|
| 类型 | 只能用于 值类型(如 int, string)。 | 可以用于任何类型(值类型或引用类型)。 |
| 初始化 | 必须在 声明时 初始化。 | 可以在 声明时 或 构造函数中 初始化。 |
| 时机 | 编译时 确定其值。 | 运行时 确定其值。 |
| 所属 | 默认是 static 的,但不能显式加 static。 | 属于实例的字段(除非显式加 static)。 |
详细解释: const 是编译期常量,其值被嵌入到程序集元数据中。readonly 是运行时常量,每个实例可以有不同的只读值(如果未加 static)。
79. C# 异常处理中,throw 和 throw ex 有什么区别?
答:
throw(不带参数): 重新抛出当前捕获的异常。- 优点: 保留了原始异常的堆栈跟踪信息,使得调试器可以追踪到异常最初发生的位置。这是推荐的重新抛出异常的方法。
throw ex(或throw new Exception(...)): 抛出新的异常对象,或抛出传入的异常对象。- 问题: 如果
ex是从catch块参数获取的,throw ex会 重置堆栈跟踪信息 ,使异常的最初来源指向throw ex语句所在行,导致调试困难。
- 问题: 如果
80. C# 中 try-catch-finally 的作用,何时可以省略 catch 或 finally?
答:
- 作用: 用于捕获和处理程序运行时可能发生的异常,确保程序不会因为未处理的错误而崩溃。
- 省略情况:
- 可以省略
catch块: 如果程序不需要处理异常,但需要确保在try块执行完成后进行清理工作,可以使用try-finally结构。 - 可以省略
finally块: 如果程序不需要执行任何清理代码,可以使用try-catch结构。 - 不能同时省略
catch和finally:try块必须至少跟随一个catch或一个finally块。
- 可以省略
81. 在 C# 中,抽象类与接口的异同。
答:
| 特性 | 抽象类 (abstract class) | 接口 (interface) |
|---|---|---|
| 成员实现 | 可以有抽象成员(无实现)和非抽象成员(有实现)。 | 传统上所有成员都是隐式抽象且无实现。(C# 8.0 起支持默认实现) |
| 字段 | 可以定义字段和常量。 | 不能 定义字段(只能定义属性、方法、事件等)。 |
| 继承数量 | 类只能 单继承 一个抽象类。 | 类可以 多实现 多个接口。 |
| 构造函数 | 可以有构造函数(用于初始化派生类中基类部分)。 | 不能 有构造函数。 |
共同点: 两者都不能直接实例化;都可以包含抽象成员(待派生类实现)。
82. C# 中 public static const int A = 1; 语句有没有错误?
答:
- 有错误。
错误原因:
const关键字默认隐式具备static属性,因此不能再显式地使用static修饰符。- 正确写法:
public const int A = 1;
83. C# 中 goto 语句的使用。
答:
- 用途:
goto语句用于将程序控制权直接转移到程序中的另一个标记语句。 - C# 中的限制:
goto只能跳转到局部范围内的标签,不能跳转到外部块、finally块或跨越变量初始化。 - 建议:
goto语句会使代码难以理解和维护,被认为是 有害 的。在现代 C# 编程中,应尽量避免使用goto,而是使用break、continue或重构为更清晰的循环和方法结构。
84. try { } catch (Exception ex) { } 在实际项目中应该如何使用?
答:
在实际项目中,异常处理应该遵循以下原则:
- 针对性捕获: 避免捕获过于宽泛的
Exception。应捕获具体的异常类型(如IOException,SqlException),以便进行针对性的处理。 - 不处理则不捕获: 只有当你能够有意义地处理(如恢复、记录、向用户友好的方式报告)异常时才捕获。如果只是吞掉异常(空
catch块),则会导致 Bug 难以发现。 - 清理资源: 使用
finally块或using语句确保非托管资源得到释放。 - 记录和重抛出: 在低级别捕获异常时,应记录完整的异常信息(包括堆栈跟踪),然后使用
throw;重新抛出,让更上层的代码决定如何处理。
示例(推荐做法):
csharp
try
{
// 业务逻辑代码
}
catch (FileNotFoundException ex)
{
// 记录详细错误日志
Logger.LogError("文件未找到错误", ex);
// 向上层抛出更友好的异常
throw new ApplicationException("无法加载配置,请检查文件路径。", ex);
}
catch (Exception ex)
{
// 捕获未预期的异常,记录日志
Logger.LogCritical("发生未处理的致命错误", ex);
// 重新抛出原始异常,保留堆栈信息
throw;
}
85. C# 中 sealed class 有什么特点?
答:
- 特点:
sealed class(密封类)是不能被继承的类。 - 作用:
- 安全性: 防止恶意或不当的子类化,锁定类的行为。
- 版本控制: 确保基类更新不会意外地影响到继承它的子类(因为它不可能有子类)。
- 性能: 编译器和 JIT 编译器知道密封类不会被派生,可以进行额外的优化,例如将虚方法调用转换为非虚方法调用。
86. C# 中的值类型(Value Type)和引用类型(Reference Type)的区别?
答:
| 特性 | 值类型 (Value Type) | 引用类型 (Reference Type) |
|---|---|---|
| 存储位置 | 栈(Stack)或对象的内部。 | 堆(Heap)。 |
| 赋值操作 | 复制 实际值。 | 复制 引用地址。 |
| 继承 | 只能继承 System.ValueType。 | 继承 System.Object,可以继承其他类。 |
| 默认值 | 0、false、null 结构等。 | null。 |
| 类型示例 | struct(结构体)、enum(枚举)、int、bool 等。 | class(类)、string、array(数组)、delegate 等。 |
87. 解释 HashTable 和 Dictionary 的区别。
答:
在 C# 中,两者都用于存储键值对,但有本质区别:
| 特性 | HashTable | Dictionary<TKey, TValue> (泛型) |
|---|---|---|
| 类型安全 | 非类型安全(存储 object)。 | 类型安全(键和值在编译时确定)。 |
| 性能 | 性能稍低(需要装箱/拆箱操作)。 | 性能更高(避免了装箱/拆箱)。 |
| 命名空间 | System.Collections (非泛型)。 | System.Collections.Generic (泛型)。 |
| 线程安全 | 默认线程安全(通过 Synchronized 方法实现)。 | 默认非线程安全。 |
结论: 在现代 C# 开发中,应始终优先使用 Dictionary<TKey, TValue>,因为它提供了类型安全和更好的性能。
88. C# 中 string 和 String Build 的区别?
答:
| 特性 | string (System.String) | StringBuilder (System.Text.StringBuilder) |
|---|---|---|
| 可变性 | 不可变(Immutable)。每次修改都会创建一个新的 string 对象。 | 可变(Mutable)。在原对象上进行修改,不会创建新的对象。 |
| 性能 | 适用于少量字符串操作。大量操作会导致频繁的内存分配和 GC 压力。 | 适用于大量循环或拼接字符串的场景,性能高。 |
| 内存 | 每次拼接都在堆上分配新内存。 | 维护一个内部可变缓冲区,减少内存分配。 |
结论: 当需要对字符串进行 10 次以上拼接或修改时,应使用 StringBuilder 以优化性能和内存使用。
89. C# 中 final、finally、finalize 的区别?
答:
这个问题在第 45 题已详细回答,这里简述 C# 对应关系:
final(Java 关键字): 对应 C# 中的sealed(限制继承)、const(编译时常量)或readonly(运行时只读字段)。finally: C# 中的finally块,确保在异常处理中执行清理代码。finalize(Java 方法): 对应 C# 中通过析构函数(例如~MyClass())实现的Finalize()方法,用于 GC 回收前的非托管资源清理。
90. 为什么说 string 是引用类型但表现得像值类型?
答:
- 是引用类型:
string是一个类(System.String),它在堆上分配内存,变量存储的是对象的引用地址。 - 表现得像值类型:
- 相等比较: C# 默认重载了
==运算符,使其比较两个字符串的 值(内容)是否相等,而不是它们的引用地址。 - 不可变性: 字符串是不可变的。当您对字符串进行"修改"(例如
s = s + "end")时,CLR 实际上是创建了一个新的字符串对象,并将s的引用指向这个新对象,而不是修改原始对象。这种行为模仿了值类型在赋值时的"复制"特性。
- 相等比较: C# 默认重载了
91. 什么是 DAL、BLL、UIL?
答:
这是传统三层架构(Three-Tier Architecture)中的三个层次:
- DAL (Data Access Layer / 数据访问层):
- 作用: 直接与数据库进行交互,负责数据的增、删、改、查(CRUD)操作。
- 职责: 连接数据库、执行 SQL、存储过程、返回结果集。
- BLL (Business Logic Layer / 业务逻辑层):
- 作用: 实现系统的核心业务规则、流程控制和业务逻辑。
- 职责: 调用 DAL 方法,处理数据校验、计算、事务管理。
- 地位: 向上为 UIL 提供服务接口,向下调用 DAL。
- UIL (User Interface Layer / 用户界面层):
- 作用: 负责与用户交互,显示数据、接收用户输入。
- 职责: 调用 BLL 提供的接口,处理用户界面的展示和事件。
92. C# 里面有多少种循环?
答:
C# 中主要的循环结构有:
for循环: 适用于已知循环次数的场景。foreach循环: 适用于遍历集合(实现IEnumerable接口的对象)。while循环: 适用于在循环开始时进行条件判断的场景。do-while循环: 适用于至少执行一次,然后在循环结束时进行条件判断的场景。
93. 什么时候使用 try/catch 语句?
答:
应在以下场景中使用 try/catch 语句:
- 调用可能失败的外部资源 API: 例如文件 I/O、数据库操作、网络通信、Web 服务调用。
- 进行类型转换或解析操作: 例如
int.Parse()可能会抛出FormatException或OverflowException。 - 预见到可能发生的程序逻辑异常: 例如数组越界(尽管通常是 Bug,但在某些算法中可能需要捕获)。
核心原则: 只有在有能力从异常中恢复、或需要对异常进行记录和报告时,才应该使用 try/catch。
94. 委托中 += 和 = 的含义?
答:
+=(添加委托): 用于向委托链中 添加 一个新的方法引用。- 作用: 订阅事件。当委托被调用时,新添加的方法也会被执行。
=(移除委托): 用于从委托链中 移除 一个方法引用。- 作用: 取消订阅事件。如果委托链中只剩下最后一个方法,移除后委托可能变为
null。
- 作用: 取消订阅事件。如果委托链中只剩下最后一个方法,移除后委托可能变为
详细解释:
委托是 多播委托 (Multicast Delegate),可以引用多个方法。+= 和 -= 操作符就是用来管理这个方法列表的。
95. C# 中 ref 和 out 关键字的区别?
答:
| 特性 | ref (引用传递) | out (输出参数) |
|---|---|---|
| 传入前初始化 | 调用方必须在传入前 初始化 变量。 | 调用方在传入前 不需要 初始化变量。 |
| 传出时赋值 | 方法内可以选择性地对参数赋值。 | 方法内 必须 对参数赋值,否则编译错误。 |
| 用途 | 双向传递:将值传入方法,并在方法内修改后传出。 | 单向传出:方法将值赋给参数,用作方法的额外返回值。 |
96. C# 中的泛型(Generics)是什么?
答:
- 泛型 允许定义类、接口、方法或委托时,将 数据类型 作为参数引入。
- 作用:
- 提高类型安全: 在编译时检查类型,避免了运行时因类型转换错误导致的异常。
- 提高性能: 避免了
object类型的装箱和拆箱操作,尤其是在处理集合时。 - 代码重用: 可以创建适用于多种数据类型的通用算法和数据结构。
97. 解释 try-catch-finally 的嵌套结构。
答:
- 嵌套结构: 允许在一个
try块、catch块或finally块的内部包含另一个完整的try-catch-finally结构。 - 作用:
- 局部异常处理: 在外层
try块中,对某个可能失败的子操作(例如文件写入)设置更精细的局部异常处理。 - 处理失败的恢复: 内层
catch块可以尝试恢复并继续执行外层try块。
- 局部异常处理: 在外层
- 执行流程: 内部的
try-catch-finally结构会优先执行和完成。如果内部抛出的异常没有被内部catch捕获,它会向上冒泡,由外部的catch块进行处理。
98. C# 中 IComparable 和 IComparer 接口的作用和区别。
答:
| 特性 | IComparable (比较对象自身) | IComparer (外部比较器) |
|---|---|---|
| 定义位置 | 由 要被排序 的类自身实现。 | 由一个 独立的外部类 实现。 |
| 作用 | 定义对象的 默认排序规则。 | 定义 多种定制化 的排序规则。 |
| 使用对象 | List.Sort() (无参数调用)。 | 传入 List.Sort(IComparer) 或 SortedList 等的构造函数。 |
| 方法 | 包含 CompareTo(T other) 方法。 | 包含 Compare(T x, T y) 方法。 |
99. 什么是 Serialization(序列化)?C# 中常用的序列化方式有哪些?
答:
- 序列化: 是将对象的状态转换为可以存储或传输的格式(如字节流、XML、JSON)的过程。
- 反序列化: 是将序列化后的格式重新转换回内存中对象的过程。
- 作用: 用于数据持久化(存储对象到文件)、跨进程/跨网络传输数据(如 Web API、Remoting)。
- C# 中常用方式:
- 二进制序列化: (
BinaryFormatter- 传统 .NET,已被弃用) - XML 序列化: (
System.Xml.Serialization.XmlSerializer) - JSON 序列化: (
System.Text.Json或Newtonsoft.Json- 目前 C# 主流 )
- 二进制序列化: (
100. 简述 LINQ (Language Integrated Query) 的基本概念和优势。
答:
- 基本概念: LINQ 是 C# 语言的扩展,它允许开发者使用统一的、类似 SQL 的语法来查询和操作各种数据源(如集合、数据库、XML 文档、
DataSet等)。 - 优势:
- 统一语法: 无论数据源是什么,查询语法都是一致的。
- 编译时检查: 由于查询被集成到语言中,因此可以在编译时而不是运行时发现查询错误。
- 类型安全: 查询结果是强类型的,避免了手动类型转换和潜在的运行时错误。
- 提高可读性和生产力: 使用声明式而非命令式代码,使数据访问逻辑更清晰简洁。