.NET/C#⾯试题汇总系列:集合、异常、泛型、LINQ、委托、EF!

1.IList 接⼝与List的区别是什么?

  • 定义与继承关系‌:

    • IList 是一个接口,定义了一组对象的集合,这些对象可以通过索引进行访问,并且支持插入、删除和搜索等操作。它继承自 ICollection 和 IEnumerable,以及它们的非泛型版本。
    • List 是一个类,实现了 IList 接口。它提供了接口中定义的所有方法的具体实现,并添加了一些额外的功能,如排序、搜索等。
  • 功能与灵活性‌:

    • 使用 IList 接口时,你的代码更加灵活,因为它不依赖于具体的实现类(如 List)。这意呀着你可以轻松地更换底层的数据存储结构,只要它实现了 IList 接口。
    • List 提供了更多的功能和性能优化,因为它是具体的实现类。但是,这也意味着你的代码与 List 紧密耦合,更换数据存储结构时可能需要更多的工作。
  • 使用场景‌:

    • 当你的代码需要处理一个集合,但你不关心这个集合是如何实现的,或者你希望你的代码能够轻松地与不同的集合实现一起工作时,使用 IList 接口是更好的选择。
    • 当你需要利用 List 提供的特定功能(如 SortFind 等)时,或者当你确定你的应用程序将始终使用 List 作为数据存储时,直接使用 List 类可能更方便。

 2.泛型的主要约束和次要约束是什么?

当⼀个泛型参数没有任何约束时,它可以进⾏的操作和运算是⾮常有限的,因为不能对实参进⾏任何类型 上的保证,这时候就需要⽤到泛型约束。泛型的约束分为:主要约束和次要约束,它们都使实参必须满⾜ ⼀定的规范,C#编译器在编译的过程中可以根据约束来检查所有泛型类型的实参并确保其满⾜约束条件。

主要约束

⼀个泛型参数⾄多拥有⼀个主要约束,主要约束可以是⼀个引⽤类型、class或者struct。如果指定⼀个引 ⽤类型(class),那么实参必须是该类型或者该类型的派⽣类型。相反,struct则规定了实参必须是⼀个 值类型。

次要约束

次要约束主要是指实参实现的接⼝的限定。对于⼀个泛型,可以有0到⽆限的次要约束,次要约束规定了实 参必须实现所有的次要约束中规定的接⼝。

public class MyGenericClass where T : class, IMyInterface // 引用类型约束和接口约束
{
    // ...
}

public interface IMyInterface
{
    // ...
}

在这个例子中,MyGenericClass的泛型类型参数T被约束为必须是引用类型,并且必须实现IMyInterface接口。这同时使用了主要约束(引用类型约束)和次要约束(接口约束)。

3. 如何把⼀个array复制到arrayist⾥?

foreach( object arr in array)
{
    arrayist.Add(arr);
}

4.List, Set, Map是否继承⾃Collection接⼝?

List, Set,是否继承自Collection接口,Map不是。

  • List:
    • 1.可以允许重复的对象。
    • 2.可以插入多个null元素。
    • 3.是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
    • 4.常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
  • Set:
    • 1.不允许重复对象
    • 2.无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
    • 3.只允许一个 null 元素
    • 4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。
  • Map:
    • 1.Map不是collection的子接口或者实现类。Map是一个接口。
    • 2.Map 的 每个 Entry 都持有两个对象,也就是一个键一个值(键值对),Map 可能会持有相同的值对象但键对象必须是唯一的。
    • 3. TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
    • 4. Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
    • 5.Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)

5. Set⾥的元素是不能重复的,那么⽤什么⽅法来区分重复与否呢? 是⽤==还是equals()? 它 们有何区别?

Set⾥的元素是不能重复的,那么⽤iterator()⽅法来区分重复与否。equals()是判读两个Set是否相等。 equals()和==⽅法决定引⽤值是否指向同⼀对像,equals()在类中被覆盖,为的是当两个分离的对象的内 容和类型相配的话,返回真值。

6.有50万个int类型的数字,现在需要判断⼀下⾥⾯是否存在重复的数字,请你简要说⼀下思路。

  1. 使用HashSet:创建一个HashSet对象,遍历整个数字集合,将每个数字添加到HashSet中。由于HashSet不允许重复元素,如果添加操作失败(即add方法返回false),则说明该数字已经存在于集合中,因此存在重复的数字。

  2. 排序后检查相邻元素:首先对数字集合进行排序,然后遍历排序后的集合,比较相邻的元素是否相等。如果发现相等的相邻元素,则说明存在重复的数字。

7.数组有没有length()这个⽅法? String有没有length()这个⽅法?

数组和字符串都使用Length属性来获取它们的长度,而不是length()方法

  • 数组没有length()这个方法。在C#中,获取数组的长度是通过其属性.Length来获取的。例如,对于一个整型数组arr,可以使用arr.Length来获取数组的长度。
  • String类有Length属性而不是length()方法。通过访问字符串对象的Length属性,可以获取字符串的长度,即字符的数量。例如,对于一个字符串s,可以使用s.Length来获取字符串的长度。

8.⼀个整数List中取出最⼤数(找最⼤值)。不能⽤Max⽅法。

遍历比较

这种方法通过遍历列表中的每个元素,并将当前元素与已知的最大值进行比较,从而找到最大值。如果当前元素大于已知的最大值,则更新最大值。

9. C#异常类有哪些信息?

C#异常类(通常继承自System.Exception类)

  1. Message 属性‌:这个属性包含了异常的描述性消息,是一个字符串类型。它是最常用于了解异常原因的属性之一。

  2. StackTrace 属性‌:这个属性提供了异常发生时调用堆栈的快照。调用堆栈是一个方法调用序列,展示了异常被抛出时的方法调用路径。这对于调试和定位问题非常有用。

  3. InnerException 属性‌:如果当前异常是由另一个异常引起的(即在一个异常的catch块中抛出了新的异常,并将原始异常作为新异常的InnerException),则此属性包含了对那个原始异常的引用。这允许你追踪异常的根源。

10. 如何创建⼀个⾃定义异常?

根据类继承原则和异常处理原则,我们可以使⽤以下⽅式来⾃定义⼀个类: 

public class CustomException : Exception
{
}

11. 利⽤IEnumerable实现斐波那契数列⽣成?

IEnumerable GenerateFibonacci(int n)
{
    if (n >= 1) yield return 1;

    int a = 1, b = 0;
    for (int i = 2; i <= n; ++i)
    {
        int t = b;
        b = a;
        a += t;

        yield return a;
    }
}

12.请利⽤ foreach 和 ref 为⼀个数组中的每个元素加 1

int[] arr= { 1, 2, 3, 4, 5 };
Console.WriteLine(string.Join(",",arr));
foreach (ref int i in arr.AsSpan())
{
    i++;
}
Console.WriteLine(string.Join(",", arr));

13.如何针对不同的异常进⾏捕捉?

  • 捕捉并处理文件读写异常
public void SafeReadFile(string filePath)
{
    try
    {
        // 尝试读取文件
        string content = System.IO.File.ReadAllText(filePath);
        Console.WriteLine($"文件内容: {content}");
    }
    catch (System.IO.FileNotFoundException ex)
    {
        Console.WriteLine($"文件未找到: {ex.Message}");
    }
    catch (System.IO.IOException ex)
    {
        Console.WriteLine($"读写文件时发生错误: {ex.Message}");
    }
    catch (Exception ex)
    {
        // 捕捉所有未处理的异常
        Console.WriteLine($"发生未知错误: {ex.Message}");
    }
}
  • 捕捉并处理网络请求异常
using System.Net.Http;
using System.Threading.Tasks;

public async Task SafeWebRequest(string url)
{
    try
    {
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response = await client.GetAsync(url);
            response.EnsureSuccessStatusCode(); // 抛出异常如果响应状态码表示失败
            string responseBody = await response.Content.ReadAsStringAsync();
            return responseBody;
        }
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine($"网络请求错误: {ex.Message}");
        return null;
    }
    catch (TaskCanceledException ex)
    {
        Console.WriteLine($"网络请求超时或被取消: {ex.Message}");
        return null;
    }
    catch (Exception ex)
    {
        // 捕捉所有未处理的异常
        Console.WriteLine($"网络请求发生未知错误: {ex.Message}");
        return null;
    }
}
  • 捕捉并处理数据库操作异常
    using System.Data.SqlClient;
    
    public void SafeDatabaseOperation(string connectionString, string query)
    {
        try
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand(query, connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }
        catch (SqlException ex)
        {
            // 根据SqlException的Number属性可以进一步区分错误类型
            Console.WriteLine($"数据库操作错误: {ex.Message}");
            if (ex.Number == -2) // 假设-2代表连接超时
            {
                Console.WriteLine("可能是数据库连接超时了。");
            }
        }
        catch (Exception ex)
        {
            // 捕捉所有未处理的异常
            Console.WriteLine($"数据库操作中发生未知错误: {ex.Message}");
        }
    }

14.如何避免类型转换时的异常?

  • 使用as关键字进行安全的类型转换:as关键字用于尝试将对象转换为指定的类型,如果转换失败,它会返回null而不是抛出异常。
    object obj = "Hello";
    string str = obj as string; // 成功转换
    if (str != null)
    {
        Console.WriteLine(str);
    }
    else
    {
        Console.WriteLine("转换失败");
    }
    
  • 使用is关键字检查对象是否为特定类型的实例:is关键字用于检查对象是否属于指定的类型或实现了指定的接口。
    object obj = "Hello";
    if (obj is string)
    {
        string str = (string)obj;
        Console.WriteLine(str);
    }
    else
    {
        Console.WriteLine("不是字符串类型");
    }
    

15.Serializable特性有什么作⽤?

Serializable 是一个属性(Attribute),用于指示一个类可以被序列化。序列化是将对象的状态信息转换为可以存储或传输的形式的过程,比如转换为二进制流或XML文档。反序列化则是这个过程的逆操作,即将存储或传输的信息恢复为原始对象。

作用:

  1. 持久化:通过将对象序列化到磁盘上,可以将其状态保存下来,以便稍后重新加载和使用。这对于需要长时间运行的程序非常有用,因为它允许程序在中断后恢复其状态。

  2. 远程调用:序列化的对象可以通过网络发送给远程计算机,然后在远程计算机上进行反序列化以恢复原始对象的状态。这允许在不同的系统之间共享数据和对象。

  3. 复制对象:序列化可以将对象转换为字节流,然后可以在内存中复制该字节流以创建对象的副本。这对于实现深拷贝非常有用,因为这样可以确保对象及其所有子对象都被正确地复制。

  4. 跨应用程序通信:序列化的对象可以作为参数传递给其他应用程序或组件,从而实现跨应用程序的通信和数据交换。

16.委托是什么?

委托是一种类型,它封装了对方法的引用。在C#中,委托(Delegate)是一种引用类型,它可以用来封装一个方法,并在需要时调用这个方法。

17.如何自定义委托?

委托(Delegate)是一种类型安全的函数指针,它允许将方法作为参数传递给其他方法,或者将方法作为其他方法的返回值。

  1. 定义委托类型
  2. ‌创建委托实例
  3. ‌调用委托
internal class Program
{
    static void Main(string[] args)
    {
        StrDelegate sd = GetTime;//2、创建委托实例
        string getsd = sd();//3、调用委托
        Console.WriteLine(getsd);
    }
    public delegate string StrDelegate();//1、定义委托类型
    public static string GetTime() {
        return DateTime.Now.ToString("HH:mm:ss");
    }
}

18 .NET默认的委托类型有哪⼏种?

Action 委托(无返回值的委托)

  • 用途‌:用于表示不返回任何值(即void返回类型)的方法。
  • 重载‌:Action 委托有多个重载版本,允许传递不同数量的参数(从0个到16个不等)。
  • 示例‌:
    static void Main(string[] args)
    {
        EAction();
        EAction("你好");
    }
    public static void EAction()
    {
        Action action = () => { Console.WriteLine("Hello world"); };
        action();
    }
    public static void EAction(string message)
    {
        Action action = (x) => { Console.WriteLine(message); };
        action(message);
    }

    Func 委托(与返回值的委托)

  • 用途‌:用于表示具有返回值的方法。
  • 重载‌:Func 委托有多个重载版本,每个版本都至少有一个输入参数(代表方法的输入),并有一个返回类型(代表方法的返回值)。输入参数的数量可以从0个到16个不等。
  • 示例‌:
    Func FAdd = (a, b) => { return a + b; };
    Console.WriteLine(FAdd(1, 2));//3
    Func FMsg = (x) => { return "Hello" + x; };
    Console.WriteLine(FMsg("小明"));//Hello小明

19.什么是泛型委托?

泛型委托是一种特殊类型的委托,它允许你定义一个委托类型,该类型可以引用任何具有特定签名的方法,而不需要事先知道这些方法的参数类型或返回类型。泛型委托提供了一种灵活的方式来处理不同类型的方法调用,并提高了代码的重用性和类型安全性。

  • 有参数有返回值的泛型委托
static void Main(string[] args)
{
    // 2、创建一个泛型委托实例,用于引用两个整数相乘的方法
    MyDelegate myDele = (x, y) => (x * y);
    //3、调用委托实例,执行乘法操作
    int result =myDele(3, 4);
}
//1、// 定义一个泛型委托,它接受两个整数参数并返回它们的乘积
public delegate TMyResult MyDelegate(T1 x,T2 y);
  • 有参数无返回值的泛型委托
static void Main(string[] args)
{
    MyDelegate my = (x) => { Console.WriteLine(x); };
    my("Hello");
}
public delegate void MyDelegate(T t);
  • 无参数无返回值的泛型委托

    

你可能感兴趣的:(c#,.net,linq)