1.IList 接⼝与List的区别是什么?
定义与继承关系:
IList<T>
是一个接口,定义了一组对象的集合,这些对象可以通过索引进行访问,并且支持插入、删除和搜索等操作。它继承自ICollection<T>
和IEnumerable<T>
,以及它们的非泛型版本。List<T>
是一个类,实现了IList<T>
接口。它提供了接口中定义的所有方法的具体实现,并添加了一些额外的功能,如排序、搜索等。功能与灵活性:
- 使用
IList<T>
接口时,你的代码更加灵活,因为它不依赖于具体的实现类(如List<T>
)。这意呀着你可以轻松地更换底层的数据存储结构,只要它实现了IList<T>
接口。List<T>
提供了更多的功能和性能优化,因为它是具体的实现类。但是,这也意味着你的代码与List<T>
紧密耦合,更换数据存储结构时可能需要更多的工作。使用场景:
- 当你的代码需要处理一个集合,但你不关心这个集合是如何实现的,或者你希望你的代码能够轻松地与不同的集合实现一起工作时,使用
IList<T>
接口是更好的选择。- 当你需要利用
List<T>
提供的特定功能(如Sort
、Find
等)时,或者当你确定你的应用程序将始终使用List<T>
作为数据存储时,直接使用List<T>
类可能更方便。
2.泛型的主要约束和次要约束是什么?
当⼀个泛型参数没有任何约束时,它可以进⾏的操作和运算是⾮常有限的,因为不能对实参进⾏任何类型 上的保证,这时候就需要⽤到泛型约束。泛型的约束分为:主要约束和次要约束,它们都使实参必须满⾜ ⼀定的规范,C#编译器在编译的过程中可以根据约束来检查所有泛型类型的实参并确保其满⾜约束条件。
主要约束
⼀个泛型参数⾄多拥有⼀个主要约束,主要约束可以是⼀个引⽤类型、class或者struct。如果指定⼀个引 ⽤类型(class),那么实参必须是该类型或者该类型的派⽣类型。相反,struct则规定了实参必须是⼀个 值类型。
次要约束
次要约束主要是指实参实现的接⼝的限定。对于⼀个泛型,可以有0到⽆限的次要约束,次要约束规定了实 参必须实现所有的次要约束中规定的接⼝。
public class MyGenericClass<T> 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类型的数字,现在需要判断⼀下⾥⾯是否存在重复的数字,请你简要说⼀下思路。
使用HashSet:创建一个HashSet对象,遍历整个数字集合,将每个数字添加到HashSet中。由于HashSet不允许重复元素,如果添加操作失败(即add方法返回false),则说明该数字已经存在于集合中,因此存在重复的数字。
排序后检查相邻元素:首先对数字集合进行排序,然后遍历排序后的集合,比较相邻的元素是否相等。如果发现相等的相邻元素,则说明存在重复的数字。
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
类)
Message 属性:这个属性包含了异常的描述性消息,是一个字符串类型。它是最常用于了解异常原因的属性之一。
StackTrace 属性:这个属性提供了异常发生时调用堆栈的快照。调用堆栈是一个方法调用序列,展示了异常被抛出时的方法调用路径。这对于调试和定位问题非常有用。
InnerException 属性:如果当前异常是由另一个异常引起的(即在一个异常的
catch
块中抛出了新的异常,并将原始异常作为新异常的InnerException
),则此属性包含了对那个原始异常的引用。这允许你追踪异常的根源。
10. 如何创建⼀个⾃定义异常?
根据类继承原则和异常处理原则,我们可以使⽤以下⽅式来⾃定义⼀个类:
public class CustomException : Exception
{
}
11. 利⽤IEnumerable实现斐波那契数列⽣成?
IEnumerable<int> 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<string> 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.如何避免类型转换时的异常?