C#常见面试题100问 (第一弹)

文章目录

      • [1. C# 中的委托(Delegate)是什么?事件(Event)是不是一种委托?](# 中的委托(Delegate)是什么?事件(Event)是不是一种委托?)
      • [2. 简述 `private`、`protected`、`public`、`internal` 修饰符的访问权限。](#2. 简述 privateprotectedpublicinternal 修饰符的访问权限。)
      • [3. `override` 与方法重载(Overload)的区别。](#3. override 与方法重载(Overload)的区别。)
      • [4. 如果在一个 B/S 结构的系统中需要传递变量值,但是又不能使用 `Session`、`Cookie`、`Application`,您有几种方法进行处理?](#4. 如果在一个 B/S 结构的系统中需要传递变量值,但是又不能使用 SessionCookieApplication,您有几种方法进行处理?)
      • [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. CTSCLSCLR 分别作何解释?)
      • [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 = nullstring 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"); 创建了几个 String Object?)
      • [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. abstractmethod 是否可同时是 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. DataReaderDataSet 有什么区别?)
      • [56. 软件开发过程一般有几个阶段?每个阶段的作用](#56. 软件开发过程一般有几个阶段?每个阶段的作用)
      • [57. 在 C# 中 `using` 和 `new` 这两个关键字有什么意义,请写出你所知道的意义?](# 中 usingnew 这两个关键字有什么意义,请写出你所知道的意义?)
      • [`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` 操作符有什么区别?](# 中的 isas 操作符有什么区别?)
      • [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` 有什么区别?](# 中 constreadonly 有什么区别?)
      • [79. C# 异常处理中,`throw` 和 `throw ex` 有什么区别?](# 异常处理中,throwthrow ex 有什么区别?)
      • [80. C# 中 `try-catch-finally` 的作用,何时可以省略 `catch` 或 `finally`?](# 中 try-catch-finally 的作用,何时可以省略 catchfinally?)
      • [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. 解释 HashTableDictionary 的区别。)
      • [88. C# 中 `string` 和 `String Build` 的区别?](# 中 stringString Build 的区别?)
      • [89. C# 中 `final`、`finally`、`finalize` 的区别?](# 中 finalfinallyfinalize 的区别?)
      • [90. 为什么说 `string` 是引用类型但表现得像值类型?](#90. 为什么说 string 是引用类型但表现得像值类型?)
      • [91. 什么是 `DAL`、`BLL`、`UIL`?](#91. 什么是 DALBLLUIL?)
      • [92. C# 里面有多少种循环?](# 里面有多少种循环?)
      • [93. 什么时候使用 `try/catch` 语句?](#93. 什么时候使用 try/catch 语句?)
      • [94. 委托中 `+=` 和 `=` 的含义?](#94. 委托中 +== 的含义?)
      • [95. C# 中 `ref` 和 `out` 关键字的区别?](# 中 refout 关键字的区别?)
      • [96. C# 中的泛型(Generics)是什么?](# 中的泛型(Generics)是什么?)
      • [97. 解释 `try-catch-finally` 的嵌套结构。](#97. 解释 try-catch-finally 的嵌套结构。)
      • [98. C# 中 `IComparable` 和 `IComparer` 接口的作用和区别。](# 中 IComparableIComparer 接口的作用和区别。)
      • [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. 简述 privateprotectedpublicinternal 修饰符的访问权限。

答:

修饰符 访问权限 适用范围
public 完全公开 任何代码,包括同一程序集和其他程序集。
internal 程序集内部 只有在 同一程序集 (Assembly) 内的代码可以访问。
protected 保护成员 只有在 类的内部派生类 的内部才可以访问。
private 私有成员 只有在 定义该成员的类的内部 才可以访问。

详细解释:

  • internal 权限常用于库的开发,它可以对外部程序集隐藏实现细节。
  • protected internal(组合修饰符)允许在当前程序集内的任何地方,或在派生自包含类的其他程序集内的类中访问。

3. override 与方法重载(Overload)的区别。

答:

特性 重载(Overload) 重写(Override)
目的 适应不同参数需求,提供多种调用方式(编译时多态)。 改变继承自基类的行为,实现多态性(运行时多态)。
签名 名称相同参数列表必须不同 名称和参数列表必须与基类方法完全相同
关键字 无特殊关键字。 派生类使用 override,基类方法必须为 virtual 或 abstract。

4. 如果在一个 B/S 结构的系统中需要传递变量值,但是又不能使用 SessionCookieApplication,您有几种方法进行处理?

答:

除了不能使用的三种方法外,主要有以下几种方法:

  1. QueryString(URL 参数): 通过 URL 传递参数,如 Page2.aspx?id=123
  2. Server.Transfer 在服务器端进行页面跳转,将当前请求的上下文传递给新的页面,新页面可以访问前一个页面的控件和变量。
  3. ViewState 存储在页面隐藏字段中的数据,用于在同一页面回发(Postback)之间维护控件状态,(严格来说是在页面内部传递)。
  4. 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 的特殊属性来实现,并使用 getset 访问器来存取数据。
  • 索引类型: 不是,索引器不限于使用数字( int)进行索引。 它可以接受任何有效的 C# 类型作为索引参数,例如 stringGuid 或自定义结构体。

详细解释:

  • 语法示例: 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

详细解释:

  1. 创建 B 实例时,先调用基类 A 的构造函数 A()
  2. A() 中调用了 virtual 方法 PrintFields(),根据运行时多态性,实际执行的是派生类 Boverride 方法。
  3. 在执行 B.PrintFields() 时:
    1. B 的实例字段(x=1y 默认值 0)已初始化。
    2. B 的构造函数体 (y=-1;) 尚未执行。
  4. 因此输出 x=1,y=0。随后 B 的构造函数体执行,y 变为 1,但此时输出已完成。

10. CTSCLSCLR 分别作何解释?

答:

  • 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 页面之间传递值的几种方式。

答:

  1. QueryString 通过 URL 传递(简单,但长度有限,不安全)。
  2. Session 变量: 存储在服务器端,每个用户独立(简单,但可能因 IIS 回收或配置丢失)。
  3. Server.Transfer 在服务器端重定向,保留 HttpContext 上下文。
  4. Cookie 存储在客户端(简单,但可能被禁用或伪造)。
  5. Application 变量: 存储在服务器端,所有用户共享(全局,但需要处理并发)。
  6. PostBackUrl PreviousPage 属性: 用于跨页面回发。
  7. 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 的方法有哪些?

答:

  1. 添加 Web 引用(Add Web Reference): 在 Visual Studio 中,通过向项目添加 Web 服务引用,工具会生成客户端代理类,允许像调用本地方法一样调用 Web 服务。
  2. 使用命令行工具(如 WSDL.exe): 通过命令行工具生成代理类。

19. .NET Remoting 的工作原理是什么?

答:

  • .NET Remoting 是一种用于在不同的 应用程序域进程计算机 之间进行通信的机制,它允许对象像本地对象一样被远程调用。
  • 工作原理: 服务器端对象通过 通道 (Channel,如 TCP 或 HTTP)注册。客户端通过 代理对象 (Proxy)与远程对象通信。客户端调用代理对象的方法时,代理将方法调用信息 序列化 ,通过通道发送到服务器端。服务器端 反序列化 信息,执行实际方法,然后将结果序列化并返回给客户端。

20. 在 C#中,string str = nullstring 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 相似)。根据您的知识,请简要谈一下您的思路。

答:

核心思路是利用 ListViewListViewItemSorter 属性和 ColumnClick 事件。

  1. 事件处理: 监听 ListViewColumnClick 事件。
  2. 实现 IComparer: 创建一个自定义的比较器类(例如 ListViewColumnSorter),该类必须实现 System.Collections.IComparer 接口。
  3. 比较逻辑:IComparerCompare 方法中,根据传入的列索引、排序方向(升序/降序)和待比较的两个 ListViewItem,获取其子项文本,并使用 String.Compare 或适当的类型比较逻辑(如 int.ParseDateTime.Parse)进行比较。
  4. 应用排序器: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. 面向对象的语言具有哪三种基本特性?

答:

面向对象的三大基本特性是:

  1. 封装 (Encapsulation)
  2. 继承 (Inheritance)
  3. 多态 (Polymorphism)

详细解释:

  • 封装: 将数据和方法捆绑到类中,并通过访问修饰符隐藏实现细节。
  • 继承: 允许子类继承父类的属性和方法,实现代码重用。
  • 多态: 允许不同类的对象对同一消息做出不同的响应(C# 主要通过重载和重写实现)。

28. 能用 foreach 遍历访问的对象需要实现哪个接口或声明哪个方法?

答:

能用 foreach 遍历访问的对象需要:

  1. 实现 System.Collections.IEnumerable 接口;或
  2. 声明一个公共的、无参数的 GetEnumerator() 方法,该方法返回一个实现了 System.Collections.IEnumerator 接口的对象。

详细解释:

C# 编译器支持 迭代器模式 。如果一个类不实现 IEnumerable 但包含一个返回 IEnumeratorGetEnumerator() 方法,编译器也允许对其使用 foreach 循环。


29. GC 是什么?为什么要有 GC?

答:

  • GC(Garbage Collector / 垃圾收集器): 是 .NET CLR 的一个核心组件,负责自动管理应用程序的内存。
  • 为什么要有 GC:
    • 避免内存泄漏: 程序员无需手动跟踪和释放不再使用的内存,防止程序长时间运行导致的内存耗尽。
    • 提高开发效率: 减轻了程序员进行繁琐且易错的内存管理的负担,可以专注于业务逻辑。
    • 提高安全性: 通过自动回收机制,GC 确保了类型安全和内存访问的有效性

30. String s = new String("xyz"); 创建了几个 String Object?

答:

  • 创建了两个 String 对象:
  1. 一个对象是 字符串常量池 (String Intern Pool)中可能已存在的 "xyz" 字符串字面量(如果不存在,则创建一个)。
  2. 另一个对象是通过 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)?

答:

  1. 接口是否可继承接口? 可以。一个接口可以继承一个或多个其他接口。
  2. 抽象类是否可实现接口? 可以 。抽象类可以实现一个或多个接口,它可以实现接口的所有成员,也可以将部分或全部接口成员声明为 abstract,留给非抽象的派生类实现。
  3. 抽象类是否可继承实体类? 可以。抽象类可以继承非抽象的实体类(但实体类必须提供构造函数)。

33. 构造器(Constructor)是否可被 override?

答:

  • 构造器不能被 override(重写)。

详细解释:

  • 构造函数不属于类的继承成员,不能被标记为 virtualoverride
  • 构造函数可以被 重载(Overload),即在同一个类中定义多个名称相同但参数列表不同的构造函数。

34. 是否可以继承 String 类?

答:

  • 不可以。

详细解释:

  • 在 C# 中,System.String 类是使用 sealed(密封) 关键字修饰的。
  • sealed 类不能被其他任何类继承,以保证类型安全和运行时优化。

35. try{} 里有一个 return 语句,那么紧跟在这个 try 后的 finally {} 里的代码会不会被执行,什么时候被执行,在 return 前还是后?

答:

  • finally 块会执行。
  • 执行时间是在 try 块的 return 语句将控制权返回给调用者之前 执行。

详细解释:

finally 块用于放置清理代码,保证无论 try 块中是否发生异常,或是否包含 returnbreakcontinue,这部分代码都会被可靠地执行。


36. 两个对象值相同(x.Equals(y) == true),但却可有不同的 hash code,这句话对不对?

答:

  • 不对。

详细解释:

  • 哈希码协定(Hash Code Contract): 在 .NET 中,如果两个对象被认为相等(即 Equals() 返回 true),那么它们的 GetHashCode() 方法必须返回相同的哈希码
  • 原因: 如果 Equals 为真但哈希码不同,将导致对象在基于哈希码的集合(如 DictionaryHashSet)中无法被正确查找。

37. switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 string 上?

答:

  1. byte 可以 。它可隐式转换为 int
  2. long 不可以switch 表达式必须是整数类型(如 sbyte, byte, short, ushort, int, uint)、charenumstring 类型。
  3. 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. abstractmethod 是否可同时是 static,是否可同时是 native,是否可同时是 synchronized?

答:

  • 都不能。

详细解释(C#):

  1. abstract static abstract 方法必须在派生类中被重写(override),但 static 方法属于类本身,不能被重写。两者是冲突的。
  2. abstract synchronized abstract 方法没有方法体,而 synchronized(C# 中的 lock)是关于方法体执行时的同步机制。无体之法无需同步。
  3. native 这是 Java 关键字,在 C# 中没有直接对应,但 C# 中的 extern 关键字表示方法由外部代码实现。abstract 方法不能是 extern,因为 abstract 要求在派生类中提供实现,而 extern 表示实现已经在外部。

40. List, Set, Map 是否继承自 Collection 接口?

答:

这是一个 Java 的概念,对应到 C# 的 集合接口 如下:

  1. List 对应 C# 的 System.Collections.Generic.IList<T>,它继承自 ICollection<T>
  2. Set 对应 C# 的 System.Collections.Generic.ISet<T>,它继承自 ICollection<T>
  3. Map 对应 C# 的 System.Collections.Generic.IDictionary<TKey, TValue>,它 不直接继承自 ICollection<T>,但它继承自 IEnumerable 接口,并且其内部的 KeysValues 属性是 ICollection 类型的。

结论: 在 C# 中,ListSet 继承自 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; 有什么错?

答:

  1. short s1 = 1; s1 = s1 + 1;:有错。
    1. 错误原因: 在 C# 中,整数常量 1 默认为 int 类型。s1 + 1 的结果是 int 类型。将 int 类型赋值给 short 类型属于 隐式的大范围向小范围转换,C# 编译器不允许,因为它可能导致数据丢失。
    2. 正确修改: 必须进行显式类型转换:s1 = (short)(s1 + 1);
  2. short s1 = 1; s1 += 1; 无错 (正确)。
    1. 原因: 复合赋值运算符(如 +=, =, = 等)在 C# 中会自动包含一个隐式的类型转换。编译器会自动生成代码 s1 = (short)(s1 + 1);

45. 谈谈 final, finally, finalize 的区别。

答:

这是 Java 概念,对应到 C# 中的概念是 sealed/const/readonlyfinallyFinalize() 方法。

概念 对应 C# 关键字/方法 作用
final (Java) sealed / const / readonly 用于 限制:sealed 限制类不能被继承;const 限制值不能被修改(编译时常量);readonly 限制字段只能在构造函数中初始化。
finally finally 块 异常处理结构的一部分,用于放置 必须执行 的清理代码,无论是否发生异常。
finalize (Java) Finalize() 方法(通过析构函数 ~ClassName() 实现) 由 GC 在回收对象内存之前调用,用于执行非托管资源(如文件句柄、数据库连接)的 最后清理操作

详细解释(C#): C# 推荐使用 IDisposable 接口和 using 语句进行确定性资源清理,而不是依赖不确定的 Finalize() 方法。


46. 如何处理几十万条并发数据?

答:

处理高并发大量数据的系统问题通常需要一个综合解决方案,包括:

  1. 数据库层面:
    1. 使用存储过程/事务: 确保数据操作的原子性和一致性。
    2. 数据库连接池: 减少连接创建和释放的开销。
    3. 优化 SQL 语句和索引: 加快查询速度。
    4. 读写分离/分库分表: 分散数据库压力。
  2. 应用架构层面:
    1. 异步编程(Async/Await): 释放线程,提高服务器并发处理能力(I/O 密集型操作)。
    2. 消息队列(MQ): 将高并发的请求(如订单)削峰填谷,解耦系统,转同步处理为异步处理。
    3. 缓存(Redis/Memcached): 缓存热点数据,减少数据库访问。
    4. 负载均衡: 将请求分配到多台服务器上。

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) 具有显著进步,主要体现在:

  1. 编译型 vs 解释型: ASP.NET编译型 (代码先编译成 IL 存储在程序集中),性能远高于 ASP 的 解释型 脚本代码。
  2. 语言支持: ASP.NET 支持多种 CLS 兼容语言(C#, VB.NET),而 ASP 仅支持 VBScript 或 JScript。
  3. 代码分离: 支持 Code-Behind(代码后置)技术,将 UI 和业务逻辑分离,提高了代码的可维护性和可读性。
  4. 面向对象: ASP.NET 提供了强大的面向对象框架(如 Page 类、控件模型),ASP 则是基于过程和脚本的。
  5. 安全性: 内建了更强大的安全特性和配置。

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 请求方式:

  1. GET
    1. 特点: 参数通过 URL(Query String)传递,数据量小,可见。常用于获取资源。
  2. POST
    1. 特点: 参数放在 HTTP 请求体中传递,数据量大,相对安全(不可见于 URL)。常用于提交/修改数据。

详细解释:

此外,现代 Web API/RESTful 服务还广泛使用 PUT (更新/替换)、DELETE(删除)等 HTTP 方法。在传统 WebForms 中,GET 常用于链接跳转,POST 常用于按钮提交。


55. DataReaderDataSet 有什么区别?

答:

特性 DataReader DataSet
内存 流式、非缓存。直接从数据库读取。 内存缓存(虚拟数据库)。存储所有数据、表、关系。
连接 连接导向。读取数据时必须保持数据库连接处于打开状态。 断开式。一旦数据填充完毕,即可关闭数据库连接。
操作 只进、只读。一次只能访问一行数据。 可随机访问、筛选、排序、修改数据。
性能 高性能,读取速度快,内存消耗低。 性能相对较低,内存消耗高(需存储所有数据)。

详细解释: 简单地讲,DataReader 适用于快速、一次性读取大量数据的场景;DataSet 适用于需要离线操作、复杂关系或多次访问数据的场景。


56. 软件开发过程一般有几个阶段?每个阶段的作用

答:

软件开发生命周期(SDLC)通常包括以下主要阶段(以瀑布或迭代模型为基础):

  1. 需求分析(Requirement Analysis): 确定软件需要做什么,形成详细的需求文档。
  2. 架构设计(Architecture Design): 定义软件的整体结构、组件、接口和数据。分为高层设计和详细设计。
  3. 编码/实现(Implementation): 依据设计文档编写代码。
  4. 测试/质量保证(Testing/QA): 验证软件是否满足需求,发现并修复缺陷。
  5. 部署/发布(Deployment): 将软件安装、配置到生产环境。
  6. 维护(Maintenance): 软件发布后的修改、升级和支持。

57. 在 C# 中 usingnew 这两个关键字有什么意义,请写出你所知道的意义?

答:

using 关键字:

  1. using 指令(引入命名空间): 用于导入命名空间,使得可以直接使用该命名空间下的类型,而无需完整限定。
    1. 示例:using System.Text;
  2. using 语句(非托管资源): 用于定义一个范围,在该范围结束时,会自动调用实现了 IDisposable 接口的对象的 Dispose() 方法,确保非托管资源 (如文件流、数据库连接)得到及时和确定的释放。
    1. 示例:using (var file = new FileStream(...)) { ... }

new 关键字:

  1. 创建实例: 用于调用构造函数,在堆上分配内存并初始化新的对象实例。
    1. 示例:var obj = new MyClass();
  2. 隐藏基类成员: 在派生类中,使用 new 修饰符来隐藏(Shadowing)基类中同名的非 virtual 成员(字段、方法、属性等)。
    1. 示例:public new void MyMethod() { ... }
  3. 类型约束: 在泛型定义中,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 之后实现线程安全单例的最佳实践。它保证了:

  1. 延迟加载: 只有在第一次访问 Instance.Value 时才创建实例。
  2. 线程安全: 默认情况下,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# 中的 isas 操作符有什么区别?

答:

操作符 作用 结果 异常
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?它有什么优缺点?

答:

  • ViewStateASP.NET WebForms 用于在 同一页面 的两次回发(Postback)之间,自动维护页面和控件状态的机制。
  • 工作原理: 状态数据被序列化为一个 Base64 编码的字符串,存储在一个名为 __VIEWSTATE 的隐藏字段中,随 HTTP 请求往返于客户端和服务器。
  • 优点:
    • 无需 SessionCookie,服务器端无存储压力。
    • 简单易用,自动维护控件状态。
  • 缺点:
    • 增加带宽和延迟: 隐藏字段数据量大,增加了页面传输大小。
    • 安全风险: 存储在客户端,可能被篡改(尽管默认情况下会进行 MAC 校验)。

74. 写出 using System.Threading.Thread; 在 C# 中的主要作用。

答:

  • 作用: 导入 System.Threading 命名空间,从而可以使用其中的类型,最主要的是 Thread 类。
  • Thread 类作用: 允许开发者创建和控制新的执行线程,实现并发编程。
  • 主要方法/属性:
    • Start(): 启动线程。
    • Sleep(): 暂停当前线程。
    • Join(): 阻塞调用线程,直到该线程终止。
    • IsBackground: 设置或获取线程是否为后台线程。

75. .NET Remoting 的生命周期是怎么管理的?

答:

.NET Remoting 中远程对象的生命周期由 租约(Leasing) 机制管理,而不是直接依赖 GC。

  1. 租约(Lease): 每个远程对象都有一个关联的租约,类似于对象的"生存时间"。
  2. 初始租约时间: 远程对象创建时有一个初始的租约时间(默认为 5 分钟)。
  3. 续约(Renewal): 当客户端调用远程对象的方法时,租约管理器会延长该对象的租约时间。
  4. 过期与回收: 如果租约时间到期且没有客户端续约,对象将被标记为回收。租约管理器会在一段时间后将对象放入 GC 队列等待回收。

76. 什么是 ADO.NET

答:

  • ADO.NET (ActiveX Data Objects .NET): 是 .NET Framework 中用于与数据源(如数据库)进行交互的一组类、接口和抽象。
  • 特点: 采用断开式 的数据访问模型,但同时也支持传统的连接式(流式)访问。
  • 核心组件: DataReader(连接式)、DataSet(断开式)、ConnectionCommandDataAdapter

77. 解释 sealed 关键字在 C# 中的用途。

答:

sealed 关键字用于修饰 方法属性

  1. 修饰类:sealed 修饰的类称为密封类 ,它不能被其他任何类继承。
    1. 用途: 防止继承破坏类的设计,或出于安全考虑,以及实现某些性能优化。
  2. 修饰方法/属性: 在派生类中,用于修饰 重写( override 的方法或属性。一旦方法被 sealed,则后续的派生类将不能再重写此方法。
    1. 用途: 锁定继承链中某个点的行为实现。

78. C# 中 constreadonly 有什么区别?

答:

特性 const (常量) readonly (只读字段)
类型 只能用于 值类型(如 int, string)。 可以用于任何类型(值类型或引用类型)。
初始化 必须在 声明时 初始化。 可以在 声明时构造函数中 初始化。
时机 编译时 确定其值。 运行时 确定其值。
所属 默认是 static 的,但不能显式加 static。 属于实例的字段(除非显式加 static)。

详细解释: const 是编译期常量,其值被嵌入到程序集元数据中。readonly 是运行时常量,每个实例可以有不同的只读值(如果未加 static)。


79. C# 异常处理中,throwthrow ex 有什么区别?

答:

  • throw (不带参数): 重新抛出当前捕获的异常。
    • 优点: 保留了原始异常的堆栈跟踪信息,使得调试器可以追踪到异常最初发生的位置。这是推荐的重新抛出异常的方法。
  • throw ex (或 throw new Exception(...)): 抛出新的异常对象,或抛出传入的异常对象。
    • 问题: 如果 ex 是从 catch 块参数获取的,throw ex重置堆栈跟踪信息 ,使异常的最初来源指向 throw ex 语句所在行,导致调试困难。

80. C# 中 try-catch-finally 的作用,何时可以省略 catchfinally

答:

  • 作用: 用于捕获和处理程序运行时可能发生的异常,确保程序不会因为未处理的错误而崩溃。
  • 省略情况:
    • 可以省略 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,而是使用 breakcontinue 或重构为更清晰的循环和方法结构。

84. try { } catch (Exception ex) { } 在实际项目中应该如何使用?

答:

在实际项目中,异常处理应该遵循以下原则:

  1. 针对性捕获: 避免捕获过于宽泛的 Exception。应捕获具体的异常类型(如 IOException, SqlException),以便进行针对性的处理。
  2. 不处理则不捕获: 只有当你能够有意义地处理(如恢复、记录、向用户友好的方式报告)异常时才捕获。如果只是吞掉异常(空 catch 块),则会导致 Bug 难以发现。
  3. 清理资源: 使用 finally 块或 using 语句确保非托管资源得到释放。
  4. 记录和重抛出: 在低级别捕获异常时,应记录完整的异常信息(包括堆栈跟踪),然后使用 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. 解释 HashTableDictionary 的区别。

答:

在 C# 中,两者都用于存储键值对,但有本质区别:

特性 HashTable Dictionary<TKey, TValue> (泛型)
类型安全 非类型安全(存储 object)。 类型安全(键和值在编译时确定)。
性能 性能稍低(需要装箱/拆箱操作)。 性能更高(避免了装箱/拆箱)。
命名空间 System.Collections (非泛型)。 System.Collections.Generic (泛型)。
线程安全 默认线程安全(通过 Synchronized 方法实现)。 默认非线程安全

结论: 在现代 C# 开发中,应始终优先使用 Dictionary<TKey, TValue>,因为它提供了类型安全和更好的性能。


88. C# 中 stringString Build 的区别?

答:

特性 string (System.String) StringBuilder (System.Text.StringBuilder)
可变性 不可变(Immutable)。每次修改都会创建一个新的 string 对象。 可变(Mutable)。在原对象上进行修改,不会创建新的对象。
性能 适用于少量字符串操作。大量操作会导致频繁的内存分配和 GC 压力。 适用于大量循环或拼接字符串的场景,性能高。
内存 每次拼接都在堆上分配新内存。 维护一个内部可变缓冲区,减少内存分配。

结论: 当需要对字符串进行 10 次以上拼接或修改时,应使用 StringBuilder 以优化性能和内存使用。


89. C# 中 finalfinallyfinalize 的区别?

答:

这个问题在第 45 题已详细回答,这里简述 C# 对应关系:

  1. final (Java 关键字): 对应 C# 中的 sealed(限制继承)、const(编译时常量)或 readonly(运行时只读字段)。
  2. finally C# 中的 finally ,确保在异常处理中执行清理代码。
  3. finalize (Java 方法): 对应 C# 中通过析构函数(例如 ~MyClass())实现的 Finalize() 方法,用于 GC 回收前的非托管资源清理。

90. 为什么说 string 是引用类型但表现得像值类型?

答:

  1. 是引用类型: string 是一个类(System.String),它在堆上分配内存,变量存储的是对象的引用地址。
  2. 表现得像值类型:
    1. 相等比较: C# 默认重载了 == 运算符,使其比较两个字符串的 (内容)是否相等,而不是它们的引用地址。
    2. 不可变性: 字符串是不可变的。当您对字符串进行"修改"(例如 s = s + "end")时,CLR 实际上是创建了一个新的字符串对象,并将 s 的引用指向这个新对象,而不是修改原始对象。这种行为模仿了值类型在赋值时的"复制"特性。

91. 什么是 DALBLLUIL

答:

这是传统三层架构(Three-Tier Architecture)中的三个层次:

  1. DAL (Data Access Layer / 数据访问层):
    1. 作用: 直接与数据库进行交互,负责数据的增、删、改、查(CRUD)操作。
    2. 职责: 连接数据库、执行 SQL、存储过程、返回结果集。
  2. BLL (Business Logic Layer / 业务逻辑层):
    1. 作用: 实现系统的核心业务规则、流程控制和业务逻辑。
    2. 职责: 调用 DAL 方法,处理数据校验、计算、事务管理。
    3. 地位: 向上为 UIL 提供服务接口,向下调用 DAL。
  3. UIL (User Interface Layer / 用户界面层):
    1. 作用: 负责与用户交互,显示数据、接收用户输入。
    2. 职责: 调用 BLL 提供的接口,处理用户界面的展示和事件。

92. C# 里面有多少种循环?

答:

C# 中主要的循环结构有:

  1. for 循环: 适用于已知循环次数的场景。
  2. foreach 循环: 适用于遍历集合(实现 IEnumerable 接口的对象)。
  3. while 循环: 适用于在循环开始时进行条件判断的场景。
  4. do-while 循环: 适用于至少执行一次,然后在循环结束时进行条件判断的场景。

93. 什么时候使用 try/catch 语句?

答:

应在以下场景中使用 try/catch 语句:

  1. 调用可能失败的外部资源 API: 例如文件 I/O、数据库操作、网络通信、Web 服务调用。
  2. 进行类型转换或解析操作: 例如 int.Parse() 可能会抛出 FormatExceptionOverflowException
  3. 预见到可能发生的程序逻辑异常: 例如数组越界(尽管通常是 Bug,但在某些算法中可能需要捕获)。

核心原则: 只有在有能力从异常中恢复、或需要对异常进行记录和报告时,才应该使用 try/catch


94. 委托中 +== 的含义?

答:

  • +=(添加委托): 用于向委托链中 添加 一个新的方法引用。
    • 作用: 订阅事件。当委托被调用时,新添加的方法也会被执行。
  • =(移除委托): 用于从委托链中 移除 一个方法引用。
    • 作用: 取消订阅事件。如果委托链中只剩下最后一个方法,移除后委托可能变为 null

详细解释:

委托是 多播委托 (Multicast Delegate),可以引用多个方法。+=-= 操作符就是用来管理这个方法列表的。


95. C# 中 refout 关键字的区别?

答:

特性 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# 中 IComparableIComparer 接口的作用和区别。

答:

特性 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.JsonNewtonsoft.Json - 目前 C# 主流 )

100. 简述 LINQ (Language Integrated Query) 的基本概念和优势。

答:

  • 基本概念: LINQ 是 C# 语言的扩展,它允许开发者使用统一的、类似 SQL 的语法来查询和操作各种数据源(如集合、数据库、XML 文档、DataSet 等)。
  • 优势:
    • 统一语法: 无论数据源是什么,查询语法都是一致的。
    • 编译时检查: 由于查询被集成到语言中,因此可以在编译时而不是运行时发现查询错误。
    • 类型安全: 查询结果是强类型的,避免了手动类型转换和潜在的运行时错误。
    • 提高可读性和生产力: 使用声明式而非命令式代码,使数据访问逻辑更清晰简洁。
相关推荐
发现一只大呆瓜2 小时前
Vue - @ 事件指南:原生 / 内置 / 自定义事件全解析
前端·vue.js·面试
koping_wu3 小时前
常用中间件面试汇总:Mysql、Mq、Redis、操作系统、Nacos、Es、Mybatis
mysql·中间件·面试
蒸蒸yyyyzwd3 小时前
后端面试经验
面试·职场和发展
weixin_404157683 小时前
Java高级面试与工程实践问题集(四)
java·开发语言·面试
brucelee1863 小时前
Install OpenLM AI module management on Windows
人工智能·windows
l1t3 小时前
DeepSeek总结的用 C# 构建 DuckDB 插件说明
前端·数据库·c#·插件·duckdb
江沉晚呤时4 小时前
.NET 9 快速上手 RabbitMQ 直连交换机:高效消息传递实战指南
开发语言·分布式·后端·rabbitmq·.net·ruby
程序员爱钓鱼4 小时前
Go输出与格式化核心库:fmt包完整指南
后端·面试·go
iReachers4 小时前
恒盾C#混淆加密大师 1.4.5 最新2026版本发布 (附CSDN下载地址)
c#·c#混淆·c#加密·wpf加密·winform加密