---
title: "C#基础知识"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/csharp-fundamentals/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/02-编程语言与运行时/.NET与CSharp/C#基础知识.md"
content_hash: 4b6297e6d08fab411f9feb5befec68a5458ed3246befb6a35b196a173a2b67e3
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# C#基础知识

> 创建时间：2020/4/20 16:34

* C# 1.0
    * 类
      * 类的意义
      * 面向对象
      * 值类型和引用类型
      * 类的访问权限
      * 抽象类与接口
      * 内部类
      * 局部类型（Partial）
      * 泛型
      * 字段，属性，成员变量
    * 结构体
    * 深拷贝与浅拷贝
    * 接口
      * 隐式实现
      * 显式实现
    * 委托
    * 事件
    * 属性器
    * 表达式
      * 数值和字符串
      * 调用表达式
      * 查询表达式
      * Lambda表达式
      * 表达式树
      * 表达式主体定义：Expression-bodied members
    * 语句
    * Attribute(特性，属性）
      * SerializableAttribute
      * SerializeField
      * 自定义Attribute

>   * 类（Class）
>   * 结构（Struct）
>   * 接口（Interface）
>   * 事件（Event）
>   * 属性（Property）
>   * 委托（Delegates）
>   * 表达式
>   * 语句
>   * 特性（属性）（Attribute）
>

### 类

>   * 包含变量（属性）
>   * 包含方法（行为）
>   * 继承
>   * 用来创建对象
>

#### 类的意义

继承功能，让用户可以更方便地服用代码，更方便地使用引擎
Unity脚本继承MonoBehaviour类。这个基类提供了丰富的功能和API

#### 面向对象

面向对象，可以理解为建模

#### 值类型和引用类型

**值类型（value type）** ：直接存储数据。
byte，short，int，bong，float，double，decimal，char，bool，struct。变量声明后，不管是否已经赋值，编译器为其分配内存。
**引用类型（reference type）** ：存储数据的引用，数据存储在堆中。
string和class。声明一个类时，只在栈中分配一小块儿内存用于容纳一个地址，此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时，分配堆上的空间，并把堆上空间的地址保存到栈上分配的小片空间中。

#### 类的访问权限

  * Internal：打DLL的时候，控制有些类不让用户访问到
  * Private
  * Public

#### 抽象类与接口

#### 内部类

类内部，只需要在类内部使用的类

#### 局部类型（Partial）

https://www.cnblogs.com/w-wfy/p/7206294.html
局部类型允许我们将一个类、结构或接口分成几个部分，分别实现在几个不同的.cs文件中。

> 是一个纯语言层的编译处理，编译器在编译的时候仍会将各个部分的局部类型合并成一个完整的类

**适用于**
（1）类型特别大，不宜放在一个文件中实现
（2） 一个类型中的一部分代码为自动化工具生成的代码，不宜与我们自己编写的代码混合在一起。
（3）需要多人合作编写一个类。
**注意点**
（1）局部类型只适用于类、接口、结构，不支持委托和枚举。
（2）同一个类型的各个部分必须都有修饰符 partial。
（3）使用局部类型时，一个类型的各个部分必须位于相同的命名空间中。
（4）一个类型的各个部分必须被同时编译。
（5）关键字partial是一个上下文关键字，只有和 class、struct、interface 放在一起时才有关键字的含义。因此partial的引入不会影响现有代码中名称为partial的变量。
（6)各个部分一般是分开放在几个不同的.cs文件中，但C#编译器允许我们将他们放在同一文件中。

**“累加”效应**

* * *

[Attribute1, Attribute2("Hello")]
partial class Class1{}

[Attribute3, Attribute2("Exit")]
partial class Class1{}
相当于

[Attribute1, Attribute2("Hello"), Attribute3, Attribute2("Exit")]
class Class1 {}

* * *

partial class Class2: Iinterface1, Iinterface2 {}
partial class Class2: Iinterface3 {}
partial class Class2: Iinterface2 {}
相当于
class Class2: Iinterface1, Iinterface2, Iinterface3 {}

**修饰符**
（1）各个部分上的访问修饰符必须维持一致性，不能使用相互矛盾的修饰符。
（2）如果一个部分类使用了abstract/sealed/static修饰符，那么整个类都将被视为抽象类/密封类/静态类。

#### 泛型

https://www.cnblogs.com/arxive/p/6179972.html
https://blog.csdn.net/kebi007/article/details/77800954

> **泛型** ：通过 **参数化类型** 来实现在同一份代码上操作多种数据类型。
>  **泛型编程** 是一种编程范式，它利用“参数化类型”将类型抽象化，从而以一种 **独立于任何特定类型** 的方式编写代码，实现更为灵活的复用。

  1. 泛型方法
  2. 泛型类
  3. 泛型集合
  4. 泛型委托
  5. 泛型约束

#### 字段，属性，成员变量

**字段（成员变量）** ：理解为一般意义上的变量。一般声明为private类型
**属性** ：是类展示给外部使用者的特性。由特殊方法（Get 和 Set 访问器）使得外部类访问当前类的字段。Get 和 Set 访问器允许在设置或检索属性之后验证属性值、执行其他代码或执行其他任务。

> 面向对象的编程方法是要 **抽象、封装**
>  **字段（Field)** 可以为public或private，若把字段声明为public，则外部使用者可以对其值进行直接修改，不利于类的封装。
>
>   1. **属性** 如何对其进行赋值（ **set** ）如何进行取值（ **get** ）都已经进行了封装， **如何控制操作是由类自身决定** 的。防止外部用户随意更改类成员变量的值, 做到安全限制，其优点是 **数据安全**
>   2. **属性** 可用来 **隔离** 类内部的 **变化** ，体现出 **封装性** 。（见下文:属性器）
>

```csharp
string str1 = String.Empty;
string str2 = String.Empty;

str2 = String.Intern(sb.ToString());

if((object)str1==(object)str2)
    Console.WriteLine(&quot;The strings are equal.&quot;);
else
    Console.WriteLine(&quot;The strings are not equal.&quot;);

```

> 字段与属性的特性区别
>
>   * 字段总是可读可写(readonlykeyword除外)属性可读可写(至少要有一样)
>   * 字段总是马上运行。效率高。属性还须要调用方法，效率低
>   * 字段能作为ref,out參数。属性不能够
>

> **字段** 和 **属性** 是相对于类而言的，而 **变量** 相对于方法或者语句块而言

### 结构体

**Struct** 是值类型，作为一个属性时，Struct无法对单一的成员变量赋值，只能对整个Struct赋值。
**Class** 则只要允许，即可随便赋值

```csharp
string str1 = String.Empty;
string str2 = String.Empty;

str2 = String.Intern(sb.ToString());

if((object)str1==(object)str2)
    Console.WriteLine(&quot;The strings are equal.&quot;);
else
    Console.WriteLine(&quot;The strings are not equal.&quot;);

```

> Sturct关键点：
>
>   * 是值类型
>   * 不能设置为null，且不能向结构变量分配null，除非将变量声明为可为空的值类型。？？？
>   * 声明变量时，本身就有值了
>   * 赋值时是深拷贝（将结构体分配给新变量时，将复制所有数据，并且对新副本所做的任何修改不会影响原始副本的数据。处理值类型的集合（Dictionary<string, myStruct>）时务必记住这一点。
>   * 不能局部赋值（transform.position.x不能直接赋值）？？？
>

> 其他细节：
>
>   * 结构体声明中，除非声明为const或static，否则无法初始化
>   * 不能声明无参构造函数或终结器
>   * 与类不同，无需使用new运算符进行实例化。
>   * 结构体无法继承自另一个结构或类，并且不能作为类的基类。所有的结构体都继承自ValueType，ValueType继承自Object
>   * 结构体可以实现接口
>

### 深拷贝与浅拷贝

**浅拷贝** 是指将对象中的数值类型的字段拷贝到新的对象中，而对象中的 **引用** 型字段则指 **复制它的一个引用到目标对象** 。如果改变目标对象中引用型字段的值他将反映在原始对象中，也就是说原始对象中对应的字段也会发生变化。

**深拷贝** 对于 **引用** ，将会在新对象中 **创建一个新** 的和原始对象中对应字段相同（内容相同）的 **字段** ，也就是说这个引用和原始对象的引用是 **不同的** ，我们在改变新对象中的这个字段的时候是 **不会影响到原始对象中对应字段** 的内容。

.net提供了一个 **ICloneable接口** ，Clone()方法，你可以实现它用来实现你自己的克隆方式。
MemberwiseClone()是object类中的一个方法，用来实现类的浅克隆

```csharp
string str1 = String.Empty;
string str2 = String.Empty;

str2 = String.Intern(sb.ToString());

if((object)str1==(object)str2)
    Console.WriteLine(&quot;The strings are equal.&quot;);
else
    Console.WriteLine(&quot;The strings are not equal.&quot;);

```
```csharp
string str1 = String.Empty;
string str2 = String.Empty;

str2 = String.Intern(sb.ToString());

if((object)str1==(object)str2)
    Console.WriteLine(&quot;The strings are equal.&quot;);
else
    Console.WriteLine(&quot;The strings are not equal.&quot;);

```

### 接口

```csharp
string str1 = String.Empty;
string str2 = String.Empty;

str2 = String.Intern(sb.ToString());

if((object)str1==(object)str2)
    Console.WriteLine(&quot;The strings are equal.&quot;);
else
    Console.WriteLine(&quot;The strings are not equal.&quot;);

```

#### 隐式实现

```csharp
string str1 = String.Empty;
string str2 = String.Empty;

str2 = String.Intern(sb.ToString());

if((object)str1==(object)str2)
    Console.WriteLine(&quot;The strings are equal.&quot;);
else
    Console.WriteLine(&quot;The strings are not equal.&quot;);

```

#### 显式实现

```csharp
string s = String.Empty;

```

**显示实现的方法调用时，引用的类型必须是所实现的接口类型**

错误：

```csharp
string s = String.Empty;

```

正确：

```csharp
string s = String.Empty;

```

**用法一** ：写一些系统或一些底层模块的时候，为了解决实现多个接口时可能出现方法重名的问题。

```csharp
string s = String.Empty;

```

**用法二** ：降低接口方法的访问权限

```csharp
string s = String.Empty;

```

用户创建出来的对象无法直接使用接口方法，而是把接口方法交给专门的调用器调用，避免用户错误操作。

> Notes:
>
>   * 接口类似于只有抽象成员的抽象基类。实现接口的任何类或结构都必须实现其所有成员。
>   * 接口无法直接实例化。
>   * 接口可以包含事件，索引器，方法和属性。
>   * 接口不包含方法的实现
>   * 一个类只能继承一个基类，可以实现一个或多个接口
>

### 委托

C# **委托** 比C++ **函数指针** 更安全，比Java的 **匿名类** 更简洁。
内置了Action<T>, Func<T>等委托定义，一般不需要自己定义委托

Notice：

  * 注册委托和注销委托最好成对出现
  * 委托有可能为null，所以最好在声明委托变量时，设置一个初始值，可以减少空指针异常的风险。如下：

```csharp
string s = String.Empty;

```

### 事件

事件和委托的区别： **事件只能在类的内部触发** ，只能从内向外发送事件。

声明过程：
第一步：声明委托(声明一下这个“回调函数指针列表当中的函数时什么样子的”)
第二步：声明与上述委托相关的事件

```csharp
string s = String.Empty;

```

另外，Event的属性器不是Set和Get，而是Add和Remove

```csharp
string s = String.Empty;

```

### 属性器

**用来隔离类内部的变化，体现出面向对象的封装性**
学生的Age每年都要变化一次。若根据生日自动计算，就不需要在类的外部控制Age的数值（内部自动控制）。

```csharp
string s = String.Empty;

```

### 表达式

表达式是由一个或多个操作数以及零个或多个组成的序列，其计算结果为一个值，对象，方法或命名空间

#### 数值和字符串

声名两个变量，也叫两个表达式的声明

```csharp
string s = String.Empty;

```

#### 调用表达式

```csharp
string s1 = &quot;Hello &quot;;
string s2 = s1;
s1 += &quot;World&quot;;

System.Console.WriteLine(s2);
//Output: Hello

```

#### 查询表达式

虽然C# 1.0没有支持，但是LINQ则是查询表达式

#### Lambda表达式

```csharp
string s1 = &quot;Hello &quot;;
string s2 = s1;
s1 += &quot;World&quot;;

System.Console.WriteLine(s2);
//Output: Hello

```

#### 表达式树

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/expression-trees/

#### 表达式主体定义：Expression-bodied members

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members

### 语句

任何可以用分号结尾的代码就是一个语句
类型：

  * 声明语句
  * 表达式语句
  * 选择语句：if else switch case
  * 迭代语句：do for foreach in while
  * 跳转语句：break continue default goto return yield
  * 异常处理语句： throw try-catch try-finally try-catch-finally
  * Await语句
  * Yield Return语句
  * Fixed语句
  * Lock语句
  * 空语句
  * 等等

### Attribute(特性，属性）

使用Attribute，可以在类或者方法，属性，程序集中声明一些标记，标记程序后，可以在运行时使用反射来查询特性。

##### SerializableAttribute

使用Serializable标记的类，可以进行序列化和反序列化，比如用JsonUtility或者XML库

```csharp
string s1 = &quot;Hello &quot;;
string s2 = s1;
s1 += &quot;World&quot;;

System.Console.WriteLine(s2);
//Output: Hello

```

##### SerializeField

Unity提供，可以让私有成员变量在Inspector上显示

##### 自定义Attribute

继承Attribute类即可
将定义好的Attribute标记到一个成员变量上
往往需要和反射技术一起使用，利用反射获取对象的类型信息，从而根据标记的Attribute实现相应的功能

```csharp
string s1 = &quot;Hello &quot;;
string s2 = s1;
s1 += &quot;World&quot;;

System.Console.WriteLine(s2);
//Output: Hello

```
```csharp
string s1 = &quot;Hello &quot;;
string s2 = s1;
s1 += &quot;World&quot;;

System.Console.WriteLine(s2);
//Output: Hello

```
