面向对象

https://www.youtube.com/watch?v=Bai9_LSZfAI

https://github.com/YSGStudyHards/DotNetGuide

Q1.面向对象的基本概念是什么?什么是类和对象

是一个逻辑单元,包含字段方法属性

1.构造函数是类中的一种方法,当创建类对象时自动执行
2.字段是任何类型的变量。基本上是数据。
3.属性是在私有字段的读写方面提供帮助的成员。
4.方法是包含成堆声明i的代码块。

对象是类的示例

Q2. 什么是继承,为什么继承很重要

继承是在两个类之间创建父-子关系,其子类将自动获取父级的属性和方法。继承有利于提高代码的复用性和抽象性。

Q3. 有哪些不同类型的继承

单继承、多继承、多级继承、等级继承

Q4. 如何阻止一个类被继承

使用SEALED关键字,或者使用static关键字,但是被标记为static的类中的变量也要是static

Q5. 什么是抽象

抽象是只显示需要的事物,隐藏它背后的细节

Q6. 什么是封装

封装的含义为将数据、方法和属性包装到一个单元

Q7. 什么是多态,它的类型是什么?

多态是指一个变量、对象或者是函数能够接受多种形式数据。比如方法重载。

多态的类型:

  • Compile Time: 编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。
  • Run Time:运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现。

对于非虚方法来说,调用哪个方法取决于该实例编译时的类型。但对于虚方法来说,调用哪个方法取决于该实例运行时的类型。

虚方法是用来实现多态的,一般virtual和override一起使用

https://blog.csdn.net/weixin_43381316/article/details/108112208

https://www.cnblogs.com/gygg/p/11556005.html

  1. 当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的函数是否为虚函数;
  2. 如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。
  3. 在这个实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过override关键字),如果是有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的函数。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚函数的父类为止,然后执行该父类里重载后的函数。

Q8. 什么是方法重载,有多少种方法能重载

方法重载是一种多态。重载的所有方法都以不同的方式工作。

方法重载也叫编译时多态,因为编译器知道这些重名的方法能做不同的事情

Q9. Overloading 和Overriding的区别

重载和重写的区别:

重载 重写
重载是一种多态,重载在同一个类中使用同样名称的方法,让重载的方法都以不同的方式工作。比如能够接受不同数据类型的方法。 重写是在不同的类中,使用相同名称和类型方法,让它以不同的方式工作。比如子类对父类方法的重写
重载不用使用任何关键字 重写需要使用virtualoverride关键字
重载不需要继承 重写需要继承

Q10. 方法重写和方法隐藏的区别

  • 方法重写有相同的名称和前面,但是在不同的类中
  • 方法隐藏可以使用new关键字隐藏基类中方法的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Program
{

class A
{

public void PrintFields()
{
Console.WriteLine("A");
}
}

class B : A
{

public new void PrintFields()
{
Console.WriteLine("B");
}
}

static void Main(string[] args)
{
A a = new B();
B b = new B();
}
}

结果:

A B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Program
{

class A
{
public virtual void PrintFields()
{
Console.WriteLine("A");
}
}

class B : A
{

public override void PrintFields()
{
Console.WriteLine("B");
}
}

static void Main(string[] args)
{
A a = new B();
B b = new B();

a.PrintFields();
b.PrintFields();
}
}

结果:

B B

Q11. 面向对象的优势和局限性

主要是对面向对象的继承、多态、封装、抽象的解释

优势:

  • 通过继承提高代码的重用性
  • 多态的灵活性
  • 通过隐藏和封装能够提高安全性
  • 容易将应用从小规模升级成大规模
  • 模块化,方便纠错

局限性

  • 小的应用不适合,前期设计时间开销大

面向对象和C# - 抽象类和接口

Q12. Abstract Class 和 Interface in C# .NET 抽象类和接口的区别

  • 抽象类和接口的区别:
抽象类 接口
抽象类包含方法的声明定义 接口只包含方法的声明
抽象类使用Abstract关键字 接口使用Interface关键字
抽象类能包含方法、字段、构造器以及其他类成员 接口只包含方法
抽象类不支持多重继承 接口支持多次继承
抽象类有构造函数 接口没有构造函数

Q13. 什么时候使用抽象类或者接口

接口:你知道必须有一个方法需要放在那,但是这个方法尚不知如何实现,可以在不同的派生类中用不同的方式实现的时候就用接口。

抽象类:当你确定某些方法要被怎样具体实现的时候就用抽象类,当然也可以使用abstract关键字定义方法,并在派生类类使用override中重写。

Q14. 为什么要使用接口

因为某些业务内容可能是不确定的,但是知道大概会有那些功能。

Q15. 接口可以有构造函数吗

不行,因为构造函数是一个类被创建时自动执行的,但是接口无法被创建

Q16. 可以创建抽象类或者接口的实例吗

不行,抽象类和接口只能被继承

面向对象和C# - 通用

Q17. 什么是访问修饰符,一个类中的默认访问修饰符是什么

访问修饰符是关键字。指定类、方法的可访问性

  • public
  • private
  • protected
  • internal
  • protectedinternal

Q18.什么是Boxing 和 Unboxing?

  • Boxing是将值类型转换为引用类型的过程。
  • Unboxing是将引用类型转换为值类型的过程。

Q19. “String” 和 “StringBuilder”的区别

字符串在C#中是不可变的。
这意味着如果您定义了一个字符串,那么您就不能修改它。每次给它赋值时,它都会创建一个新的字符串。

字符串创建器是可变的

这意味着,如果要对字符串执行任何操作,那么它就不会每次都创建一个新实例。

如果需要频繁地更改字符串,则StringBuilder是一个更好的选项,因为它不会每次都申请新内存。

Q20. C#中的基础字符串操作符

  • 连接:使用 + 进行两个字符串之间的连接
  • 替换:replace
  • 修剪:trim() 能够修建末端的空白字符
  • 包含:contains() 检查是否包含子串

Q21. 什么是可空类型

如果要保存空值,我们必须使用可空类型,因为变量类型不包含空值。

  • 用法:使用Nullable<int>或者int?

Q22. 解释泛型,如何使用它们

泛型允许我们让类和方法类型独立或者类型安全

使用object代替原来的类型

使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Caculator
{
public static bool AreEqual<T>(T value1, T value2)
{
return value1.Equals(value2);
}
}

static void Main(string[] args)
{
bool equal = Caculator.AreEqual(4, 4);
bool strEqual = Caculator.AreEqual("Interview", "Happy");
Console.WriteLine(equal + " " + strEqual);
}

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Caculator<T>
{
public static bool AreEqual(T value1, T value2)
{
return value1.Equals(value2);
}
}

static void Main(string[] args)
{
bool equal = Caculator<int>.AreEqual(4, 4);
bool strEqual = Caculator<string>.AreEqual("Interview", "Happy");
Console.WriteLine(equal + " " + strEqual);
}

面向对象和C# - 异常处理

Q23. 如何在C#中实现异常处理

面向对象编程中的异常处理用于管理错误

https://zhuanlan.zhihu.com/p/377036317

  1. TRY:是一个代码块,其中包含可能发生错误的代码
  2. CATCH:当TRY块中发生任何错误时,则传递给CATCH块来处理它。
  3. Finally:用于执行给定一组语句,无论是否抛出异常。
  4. THROW:CATCH块中,您可以使用throw关键字将堆栈跟踪传递到上一级别。

Q24. 能否执行多个Catch块

不行,我们可以编写多个catch块,但是只有一个会被执行。

Q25. Finally块是什么,什么时候用它

无论是否抛出异常都会执行的块,一般用在释放资源

Q26. 能否只包含 “Try” 块而没有 “Catch” 块

可以,但是必须要有Finally块

Q27. “throw ex” 和“throw”的区别是?

  • “throw ex”:抛出异常将清空堆栈跟踪,
  • “throw ”:将保存整个堆栈。

错误的做法:

1
2
3
4
5
6
7
8
9
try
{
// Some code that throws an exception
}
catch (Exception ex)
{
// some code that handles the exception
throw ex;
}

正确的做法

1
2
3
4
5
6
7
8
9
try
{
// Some code that throws an exception
}
catch (Exception ex)
{
// some code that handles the exception
throw;
}

使用“throw;”代替了“throw ex;”,后者会清空原来的堆栈跟踪信息。如果我们在抛出异常时没有指定具体的异常(简单的throw),那么它会默认地将原来捕获的异常继续上抛。这样 的话,上层代码捕获的异常还是最开始我们通过catch捕获的同一个异常。

面向对象和C# - 循环和容器

Q28. C#的循环类型

  • While
  • do While:跟While的区别是do,无论条件,会至少执行一次
  • for
  • foreach

Q29. “continue” 和 “break” 的区别?

  • Continue:用于跳过某段代码回到循环的开头继续执行
  • break:退出循环

Q30.Array 和 ArrayList 的区别

Array ArrayList
Array 是强类型,只能存储特定类型数据 ArrayList是弱类型,能存储任意类型数据
Array是固定长度 ArrayList是可变长度

Q31. Arraylist 和 Hashtable的区别

Arraylist Hashtable
Arraylist 添加数据只要添加值 Hashtable添加数据时需要添加键和值

Q32. C#中的容器是什么,它们的类型是

C#集合旨在更有效地存储、管理和操作类似的数据。例如ArrayListDictionaryListHashtable等。

image-20231208220140751

C#中的容器包括:通用容器泛型容器并发容器

concurrent:并发类型,C#中的线程安全集合

Q33. C# 中的IEnumerable是什么

IEnumerable可枚举的所有非泛型集合的基接口,当我们想要在集合类之间使用Foreach,可以使用IEnumerable接口。

比如使用List迭代某个类实例化的对象

Q34. IEnumerable 和 IEnumerator 的区别

lEnumebale包含IEnumerator 功能

image-20231208221210716

Q35. IEnumerable 和 IQueryable 区别,为什么在sql中使用IQueryable

IQueryableIEnumerable 类似,用于从数据中收集sql查询。它在SYSTEM.LINQ命名空间下。

image-20231208225226825

IEnumerable从数据库中获取信息会带来全部的信息再过滤,影响性能

IQueryable会先过滤再带来数据

面向对象和C# - 方法和委托

Q36. “out” 和“ref” 的区别

通过使用ref和out关键字,我们可以通过引用传递参数,当您想从一个方法中返回多个值时,您可以使用outref

out ref
不需要在传递out参数之前初始化它。 需要初始化
返回前必须初始化out参数 返回前不需要必须初始化

简单来说,使用out来指定一个新的值,使用ref来修改一个值。

Q37. “params” 关键字的用处

params关键字用来表示可以使用可变参数类型

当程序员对要使用的参数的数量没有任何先验知识时,比较有用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static int Add(params int[] listNumbers)
{
int total = 0;

foreach(int i in listNumbers)
{
total += i;
}

return total;
}

static void Main(string[] args)
{
int y = Add(5, 10, 15, 20);
}

Q38. 什么是构造函数,它的类型是什么?

构造函数是类的一种特殊方法,在创建类的实例时自动调用。构造函数的名称必须与类名相同,否则将很难从其他方法中识别出来。

C#的构造函数

  • Default constructor:默认构造函数,没有任何参数的构造函数
  • Parameterized constructor:参数构造函数,具有至少一个输入参数的构造函数是参数构造函数。
  • Copy constructor:复制构造函数,从另一个对象复制变量
  • Static constructor:静态构造函数,用于在调用类的任何静态成员之前进行调用。需要以Static作为类型
  • Private constructor:私有构造函数,当构造函数是Private 修饰符时,其他类是不能从该类派生,也不可能创建该类的实例。

Q39. 什么时候使用Private constructor

单例设计模式中常使用Private constructor

私有构造函数是一个特殊的构造函数,用于只包含静态成员的类中。

Q40. C#中的Extension方法是什么,什么时候用他们?

Extension Method允许在现有类中添加新方法,而无需修改原始类的源代码。比如说使用一些原本类没有的方法。

比如说RightSubstring()方法不是string类中的方法,但是我们可以通过使用扩展方法来添加它。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static class StringExtensions
{
public static string RightSubstring(this string s, int count)
{
return s.Substring(s.Length - count, count);
}
}

static void Main(string[] args)
{
string test = "Hello";
string a = test.RightSubstring(5);
}

Q41. 什么是Delegate? 什么时候用它们?

委托是一个变量,它保存对A函数或指向A函数的指针的引用。委托可以引用多个方法,需要具有相同的返回类型和参数。

简单来说委托就是对函数的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
delegate void Caculator(int x, int y);

public class Program
{

public static void Add(int x, int y)
{
Console.WriteLine(x + y);
}


static void Main(string[] args)
{
Caculator caculator = new Caculator(Add);
caculator(10, 20);
}

Q42. 什么是Multicast Delegates?

多点委托对是多个函数的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
delegate void Caculator(int x, int y);

public class Program
{

public static void Add(int x, int y)
{
Console.WriteLine(x + y);
}

public static void Mul(int x, int y)
{
Console.WriteLine(x * y);
}


static void Main(string[] args)
{
Caculator caculator = new Caculator(Add);
caculator += Mul;
caculator(10, 20);
}

执行结果为:

1
2
30
200

Q43.什么是Anonymous Delegates

Anonymous Delegates,在匿名委托中,您可以创建一个委托,但是不需要声明与其关联的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public delegate void Caculator(int x, int y);

public class Program
{
static void Main(string[] args)
{
Caculator caculator = delegate (int a, int b)
{
Console.WriteLine(a + b);
};

caculator(10, 20);
}
}

Q44. Events 和 Delegates的区别

  • 委托是一个变量,它保存对函数的引用或指向函数的指针。
  • 事件是依赖于委托的通知机制。

image-20231208234135168

事件依赖于委托,没有委托就不能被调用。事件类似于委托的包装器,以提高其安全性。

面向对象和C# - 重要关键字

  • this:此关键字用于引用类的当前的实例,避免了类字段和构造函数参数之间的名称混淆。

  • using:

    • 使用指令

      1
      using System.IO;
    • 使用语句确保即使发生异常也调用**dispose()**方法。主要用于创建数据库联系

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      static void Main(string[] args)
      {
      using(var connection = new SqlConnection("ConnectionString"))
      {
      var query = "UPDATE YourTable SET Property = value";
      var command = new SqlCommand(query, connection);

      connection.Open();
      command.ExecuteNonQuery();

      //connection.Dispose();
      }
      }
  • is 和 as 操作符:
  • is: 操作符用于检查对象的类型,是一个布尔类型
  • as:操作符用于在兼容的引用类型之间执行转换。比如在objectstring之间进行转换,不是布尔类型
  • ReadonlyConstant
  • Readonly:使用Readonly,可以在声明或者构造函数中赋值,初始化完成后便无法更改。ReadOnly是运行时常数
  • Constant:使用Constant,只能在声明中赋值,其他地方不能更改const字段的值。Constant是编译时常数
  • Static:静态类是不能被创建,不能继承的类。静态类用作静态成员,比如方法、构造函数和其他的容器
  • vardynamic
    • var:变量的类型由编译器在编译时决定。
    • dynamic:变量的类型是在运行时决定的,可以在其他地方赋值其他类型的数据
  • Enum:枚举是一种特殊的类,用来表示一组int类型常量,通常第一个常量由0开始