CShape

VS保存为UTF-8:扩展——自定义——搜索ForceUTF8

VS帮助文档:

  1. Visual Studio Installer——单个组件——Help Viewer
  2. VS——帮助——添加和移除帮助内容——索引

C# 教程 | 菜鸟教程 (runoob.com)


一、入门

1 C#特点

命名方式:

  1. 变量前类型后名字,前小写后大写,例子:intNum,strClass
  2. 函数命名大写,属性命名小写。
// 控制台输出一行(自带换行)
Console.WriteLine();
// 控制台输入一行
float num = float.Parse(Console.ReadLine());
// 控制台输出一行(不换行)
Console.Write();


// 引用命名空间
using name
// 定义命名空间
namespace name {}
// 定义类
class name {}
// 定义函数
void name {}
// 主函数
Main();

// 折叠代码
#region MyRegion
// 被折叠的代码
#endregion

2 VS操作

VS的自动补足中的含义(扳手:属性,方块:方法,长方体:字段)

Ctrl + A 全选

Ctrl + K + F 自动缩进

Ctrl + K + C 注释

Ctrl + K + U 取消注释

Ctrl + R + R 全体重命名

Ctrl + F:选择字符串替换

类中自动补足类中属性:tab + tab


二、类型

1 一般类型

详细文档

official::官方文档

N元运算符:几个变量参与运算:

  1. 一元运算符:i++
  2. 二元运算符: a+b
  3. 三元运算符:a?b:c
// 格式化输出设置
String str = string.Format("a:{0},b:{1}",a,b);
// 设置空
char a = '\0';
string a = "";

// 类型转化
x = int.Parse(a); // int(a)->x
x = a.ToString(); // string(a)->x

// 隐式转化(小->大)
byte i4 = 300;
int i3 = i4;
// 显示转化(大->小)(会丢失数据)
int i4;
byte i3 = (byte) i4;

// 不同操作方式会导致变量提升
byte b = 0;
// 不会类型提升b:byte
b += 3;
// 会类型提升为int b:int
b = b + 3;

// 依照v1值自动推断类型
var v1;
// 任意类型
object o1 = 1/true/new int[3];

2 特别类型

// 枚举类型(默认是:int类型)
enum Directon:int {up,down}
// 字符串枚举:
MyEnum enumValue = (MyEnum)Enum.Parse(typeof(MyEnum), str);
/*
枚举多选:
1.枚举开头处加上[Flags]
2.枚举值为2**n,运算时按位||
3.判断时按位&&
*/

// 可空类型(在变量名后加上?)
int? num = null;
// 此时获得值需要.Value
num.Value;

// 合并运算符(如果空返回后面的值)
num3 = num1 ?? 5.34;

3 异常

try
{}
catch( ExceptionName e)
{}
// 捕获所有异常
catch (Exception ex)
{}
finally
{}

// 抛出异常
Throw e

4 指针

// 加上unsafe修饰符即可在函数中使用指针
static unsafe void Main()
{
int var = 20;
int* p = &var;
Console.WriteLine("Data is: {0} " , p->ToString());
}


// 直接在代码块中用unsafe也能使用指针
public static void Main()
{
unsafe
{
int var = 20;
int* p = &var;
Console.WriteLine("Data is: {0} " , p->ToString());
}
}

三、结构

1 选择

if() {}  else if()  {}  else{}
switch()
{
case 1 : break;
default:
}

2 循环

for(i = 0;i < 5;i++)
while(){}
do{}while();

3 方法

void可接return;返回

///用于写方法注释

重载:同名不同参。

// 创建随机数
Random random = new Random();
// 产生随机数
int number = random.Next(1,101);

四、数组

1 一维数组

// 声明
int[] a;
// 初始化(默认值为0),变量名和类型应当一致,容量为n。
a = new int[n];

// 直接初始化
srting[] array = new string[2]{"a","b"};
// 声明+初始化+赋值
bool[] array = {true,false}

// 获取数组中的每一个元素(不可以改变元素值)
foreach(int i in array);

// 任意数组
Array arr1 = new ElemType[];

2 多维数组

// 二维数组
int [,] array = new int[,];
// 案例:5行3列
int[,] any = new int[5,3];

// 多维数组
int[,,,] array = new int[2, 2, 2, 2];

3 交错数组

定义:不规则的表格(交错数组元素的单位是行(也就是数组))

int[][] array;
array = new int[4][];
// 赋值是赋于一个数组
array[0] = new int[3];

4 参数数组

定义在方法参数中。

对方法内:数组。

对方法外:可以传入数组,可以传入一组同类型的数组,可以不传入参数。

Add(params int[] arr);

5 数组方法

// 获得该数组第 n 维的长度。
array.GetLength(n);
// 数组第 0 维的长度。
array.Length;

五、存储

1 数据类型

分类:值类型,引用类型:

  • 值类型:结构(数据类型,bool/char),枚举,声明栈存入栈。

  • 引用类型:接口,类(string,Array,委托,object),声明栈存入堆。

数组类型传入的是地址。(无需return)

bool值类型比较值,引用类型直接比引用。

// 修改的是堆区
array[0] = 2;
// 修改的是栈区
arr = new int[]{2};
// 修改的是栈区(堆中的数据不可修改)
S1 = '男';

// 值的地址(ref相当于&,等于&a)
ref int a;
// 输出参数(也是地址操作)
out int a;
/*
1.out方法内部必须赋值。
2.out传参前可以不赋值。
*/

// 尝试类型转化
bool [type].TryParse(input, out result);
/*
可转:返回true和result。
不可转:返回false,result = 0。
*/

2 存储

CG:垃圾回收器(自动回收堆中的无引用数据。可手动,一般是交互时间最少的时候回收)

箱操作:

  • 装箱:值->object(堆开辟空间,值存入堆中,返回堆地址)
  • 拆箱:object->值(判断类型,返回已经装箱的引用)
  • 非常消耗性能。(避免方法:重载,泛型)

字符串池:S1 =“ab”; S2="ab";(S1与S2相等)

每次修改字符串相当于重新开辟了空间,object同理。

// 可变字符串
StringBuilder str = new StringBuilder(初始大小);

3 文件与IO操作

概念

System.IO:文件系统对应类。

  • DriveInfo:有关驱动器信息的类。
  • File:有关对文件整体操作及内容读写的类。
  • FileInfo:和File相似,不同之处是比FILE多了一个Length属性,通过该属性可以了解一个文件所占字节数。
  • Directory:有关文件夹的处理。
  • DirectoryInfo:功能同Directory,只是所有成员为实例成员。
  • Path:专用于对路径字符串的处理。

方法

// 文件操作
File.Create();
File.Delete();
File.Move();
File.Copy(old,new,bool); // (是否覆盖原有文件)
File.Exists();
// 读文件
File.ReadAllText(path);
File.ReadAllLines(path);
File.ReadLines();
File.ReadAllBytes();
// 写文件
File.WriteAllText(path,string);
File.WriteAllLines(path,array);
File.WriteAllBytes();

// 文件夹操作
Directory.CreateDirectory(path);
Directory.Delete(path,bool); // (是否删除子文件夹)
Directory.Move();
Directory.GetFiles(); // 获取文件夹下所有文件名
Directory.GetDirectories(); // 获取文件夹下所有子文件夹名
Directory.Exists(path);

// 取得文件名和扩展名
Path.GetFileName();
// 仅取得文件名
Path.GetFileNameWithOutExtension();
// 取得路径中目录的名称
Path.GetDirectoryName();
// 多个字符串合并成一个路径
Path.Combine();

案例

// 路径
private string path = @"D:/aa/bb";

// DirectoryInfo使用
DirectoryInfo info = new DirectoryInfo(path);
if(info.Exists) info.Create();

// 流对象读写文件
using System;
using System.IO;
using System.Text;

public class FileIO
{
public static string ReadFile(string filePath)
{
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
using (StreamReader reader = new StreamReader(fs, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
catch (Exception ex)
{
Console.WriteLine("An error occurred while reading the file: " + ex.Message);
return null;
}
}

public static void WriteFile(string filePath, string content)
{
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
using (StreamWriter writer = new StreamWriter(fs, Encoding.UTF8))
{
writer.Write(content);
}
}
}
catch (Exception ex)
{
Console.WriteLine("An error occurred while writing to the file: " + ex.Message);
}
}
}


六、类与对象

my::简单线性表

my::二维数组方向

1 定义

一般情况:数据私有,方法公开。

类中方法外的变量:

  1. 具有默认值
  2. 存于堆。
  3. 可以与局部变量重名。(用this.区分)

类中的值:声明存在堆中,值也存于堆。

类中的引用:声明存在堆中,值也存在堆中,但是是存于另一个堆。

访问级别:

  • public :公有访问。不受任何限制,任何地方都可以引用,特殊场景不安全
  • private :私有访问。只限于本类成员访问,子类,实例都不能访问,可以用在本类种独有的方法。
  • protected :保护访问。只限于本类和子类访问,实例不能访问。
  • internal :内部访问。只限于本项目内访问,其他项目不能访问。
  • protected internal 组合:内部保护访问。只限于本项目或是子类访问,其他项目不能访问。

修饰符:

  • static:静态的(同类型类共用一个变量,通过类名调用)(类被加载时静态成员就存在)
// 修饰符 访问级别  class  类名(推荐首字母大写)  { 类成员... }
static public class Name(){}

2 类中成员

构造函数:(访问级别)(类名)(){ }

new的时候使用的是构造函数。

一个类无构造函数会自动给一个无参构造函数。

构造函数:this( 本构造函数参数 ) 可以调用另一个构造函数。

构造函数级别:

  • public:公开类,创建对象时初始化。
  • private:无法在类外用这个类(称为单例)。
  • static:初始化类的静态数据成员,类被加载时调用一次。
// 类 { 字段  属性  构造函数  方法 },一般类中有属性和方法即可。
public class Calculate
{
// 字段:类中元素。
private int a;
private int b;

// 属性:一般用于规定字段的读写。
// get可读,set可写。
public int A
{
get { return a; }
set { a = value; }
}
public int B
{
get { return b; }
set { b = value; }
}
// 自动属性:包含一个字段(数据)和两个方法
public int C { get; set; }

// 构造函数:new的时候调用的函数
public Calculate(int a)
{
this.A = a;
}
public Calculate(int a, int b) : this(a)
{
this.B = b;
}
// 析构函数:对象被销毁时被调用
~Calculate()
{
Console.WriteLine("End!");
}

// 方法:函数。
public int add()
{
C = A + B;
Console.WriteLine(C);
return C;
}
// 销毁自身方法,调用后触发析构函数
public void Dispose()
{
GC.SuppressFinalize(this);
}
}

3 使用

// 数据成员的引用
this.name;
// 声明Wife类型的引用
Wife wife01;
// Wife类型实例化
wife01 = new Wife();

4 继承

子类可以用父类公共和保护类型代码,父类不能用子类代码。

父类私有代码子类不可以使用。

静态类不能被继承,但是静态方法和属性可以被继承。

一个子类只可以继承一个父类,但是可以继承多个接口。

// 父类
class Animal
{
public void Eat()
{
Console.WriteLine("动物正在进食...");
}
}
// 子类,此时Dog类拥有Animal类中的公共和保护类型代码。
class Dog : Animal
{
public void Bark()
{
Console.WriteLine("狗在汪汪叫!");
}
}
class Program
{
static void Main(string[] args)
{
Dog dog = new Dog();
dog.Eat(); // 调用父类的方法
dog.Bark(); // 调用子类的方法
}
}

// 输出
// 动物正在进食...
// 狗在汪汪叫!

5 引用

父类型的引用指向子类的对象,也就是说允许你使用基类(父类)的引用或变量来指向派生类(子类)的对象。

意思是说:假设有一个父类Person,有两个子类Student,Teacher。当我按Person person02 = new Student();实例化时,person02实际上只能访问父类中的内容,除非后续强制类型转化后才能访问对应子类的内容。

// 只能使用父类成员。
Person person02 = new Student();

//如果需要访问该子类成员,需要强制类型转换。
Student stu02 = (Student)person02;

//转化成其他家族成员会抛出异常。
Teacher teacher02 =(Teacher)person02;
teacher02.Salary = 100;

//若可以运行则不抛出异常,people = null。
teacher people = student as teacher;

6 多态

静态多态性:编译时绑定方法关系。

动态多态性:运行时绑定方法关系。

// 静态多态
// 函数重载
public class Adds
{
public int Add(int a, int b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
// 运算符重载
public static Box operator+ (Box a, Box b)
{
Box res = new Box();
res.length = a.length + b.length;
return res.length;
}

// 动态多态

7 虚方法

重写虚方法就调用子类,不重写则调用父类。

// 父
protected virtual void Start()
{
...;
}

// 子
protected override void Start()
{
// 调用父类Start()
base.Start();
...;
}

8 抽象类

C# 中的抽象类(abstract class)是一种不能直接实例化的类,它主要用于定义一个基类,并为派生类提供一个公共接口和部分实现。抽象类可以包含抽象方法(没有具体实现的方法,必须由派生类重写)、抽象属性以及普通成员(如字段、非抽象方法、静态成员等)。使用抽象类的主要目的是强制子类对某些成员进行实现。

抽象类特点:

  1. abstract 关键字修饰的类是抽象类。
  2. 抽象类中可以有抽象方法(声明时使用 abstract 关键字且没有方法体)。
  3. 抽象类不能被实例化,只能作为其他类的基类来使用。
  4. 如果一个类包含至少一个抽象方法,则该类必须声明为抽象类。
  5. 派生自抽象类的非抽象子类必须实现所有抽象方法,否则子类也必须声明为抽象类。
// 定义一个抽象类 Animal,具有抽象属性 Species 和抽象方法 MakeSound
public abstract class Animal
{
public abstract string Species { get; set; } // 抽象属性

// 抽象方法,没有方法体
public abstract void MakeSound();

// 公共构造函数和其他非抽象方法可以在抽象类中定义
public Animal(string initialSpecies)
{
Species = initialSpecies;
}

public void Sleep()
{
Console.WriteLine("Zzzz...");
}
}

// Dog 类继承自抽象类 Animal,并实现了抽象方法 MakeSound
public class Dog : Animal
{
public override string Species => "Canine"; // 实现抽象属性

// 实现抽象方法
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}

// 使用:
Animal myDog = new Dog(); // 创建 Dog 类型的对象,但变量类型为 Animal
myDog.MakeSound(); // 输出 "Woof!"

9 using

使用using语句,用于自动释放实现了 IDisposable 接口的对象,以确保资源得到及时释放。

// 案例
public int ExecuteNonQuery(string sql)
{
using (sqlConnection conn= new sqlConnection(_connectionString))
{
using (sqlCommand cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = sql;
return cmd.ExecuteNonQuery();
}
}
}

七、集合

内置的数据结构。

泛型和非泛型函数名是一样的。

// 非泛型集合
using System.Collections;
// 泛型集合
using System.Collections.Generic;

1 List

// 非泛型列表
ArrayList list = new ArrayList();
// 泛型列表
List<T> list = new List<T>();

// 创建一个空的整数列表
List<int> numbers = new List<int>();
// 创建一个包含初始元素的字符串列表
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };

// 列表长度
list.Count;

// 访问元素
list[0];
// 访问末尾元素
list.Last();

// 查找指定元素的索引
IndexOf(elem);
// 判断列表是否包含指定元素
list.Contains(elem);

// 在列表末尾添加一个元素
list.Add(elem);
// 在指定索引位置插入一个元素
list.Insert(idx, elem);

// 删除指定的元素
list.Remove(elem);
// 删除指定索引位置的元素
list.RemoveAt(idx);

// 升序排序
list.Sort();
// 反转列表
list.Reverse();

2 Dictionary

字典

// 非泛型字典
Hashtable dict = new Hashtable();
// 泛型字典
Dictionary<key,value> dict = new Dictionary<key,value>();

// 构建字典(key,value)
Dictionary<key, value> dict = new Dictionary<key, value>();

// 键和值
dict.Keys;
dict.Values;

// 总项数
dict.Count

// 获得值元素的唯一标识符
value.GetInstanceID();

// 添加元素
dict.Add(key, value);
// 删除元素
dict.Remove(key);
// 清空元素
dict.Clear();

// 判断键存在
dict.ContainsKey(key);
// 判断值存在
dict.ContainsValue(value);

// 遍历Dictory并输出键值对
foreach (var pair in dict)
{
Console.WriteLine($"Key: {pair.Key}, Value: {pair.Value}");
}

3 Stack

// 非泛型栈
Stack stack = new Stack();
// 泛型栈
Stack<T> stack = new Stack<T>();

// 入栈
stack.Push("str");
// 出栈
stack.Pop();

// 读去栈顶
stack.Peek();

// 栈总数
stack.Count();

4 Queue

队列

// 非泛型队列
Queue queue = new Queue();
// 泛型队列
Queue<T> queue = new Queue<T>();

// 入队
queue.Enqueue("str");
// 出队
queue.Dequeue();

// 读队首
queue.Peek();
// 队长
queue.Count();

八、高级

1 结构体

与类语法相似,但结构体属于值类型,类属于引用类型。

区别:结构体包含的无参数构造函数要给所有属性先赋值。

适用:点,颜色等轻量级对象。

using System;

public struct Point
{
public int x;
public int y;

public Point(int x, int y)
{
this.x = x;
this.y = y;
}

public void Print()
{
Console.WriteLine($"({x}, {y})");
}
}

public class Program
{
public static void Main()
{
Point p1 = new Point(1, 2);
p1.Print(); // 输出结果为 (1, 2)

Point p2 = p1;
p2.x = 3;
p2.Print(); // 输出结果为 (3, 2)
p1.Print(); // 输出结果仍为 (1, 2)
}
}

2 接口

接口中相当于填入方法的声明,继承接口的类要实现接口的所有方法。

接口默认公开,接口不能实例化。

接口可以继承接口。类只可以继承一个基类,但是可以继承多个接口。

接口常用于实现多态,代码框架等方面。

抽象类(Abstract Class):

  1. 状态携带:抽象类可以包含字段(状态),即它可以有实例变量。这意味着抽象类可以保存状态,而不仅仅提供行为的抽象。
  2. 成员实现:抽象类可以包含非抽象方法、构造函数、属性和事件,这意味着它可以提供一些默认的行为实现,而不仅仅是签名。
  3. 继承限制:一个类只能继承一个抽象类,这是因为C#中的类继承是单继承的。这意味着如果你的类已经继承了一个基类,就不能再继承另一个抽象类。
  4. 构造函数:抽象类可以有构造函数,这在创建具体类时可能会被调用。
  5. 访问修饰符:抽象类可以有访问修饰符(如 public、protected、internal 等),这决定了它在哪些范围内可以被继承。
  6. 抽象成员:抽象类中的抽象成员(方法、属性、索引器、事件)没有实现,它们必须在派生类中被覆盖(override)。

接口(Interface):

  1. 纯抽象:接口只能包含抽象成员,不能有任何实现。这意味着接口中的所有成员(方法、属性、事件、索引器)都必须在实现该接口的类中显式地提供实现。
  2. 无状态:接口不能包含字段或构造函数,因此它不能保存状态。接口关注的是行为,而非状态。
  3. 多重继承:一个类可以实现多个接口,这弥补了C#中类的单继承限制,允许一个类拥有来自多个源的行为。
  4. 默认方法实现(C# 8.0+):从C# 8.0开始,接口可以提供默认的方法实现,这使得接口更加灵活,可以在不破坏现有代码的情况下添加新成员。
  5. 访问修饰符:接口本身没有访问修饰符,但是接口成员默认是公共的。
  6. 命名空间:接口可以存在于任何命名空间中,这有助于组织和发现相关的接口。

选择建议:

  • 当你需要定义一组相关行为,但不关心具体实现,并希望允许多重继承时,应使用接口。
  • 当你需要定义一个基类,它可以包含部分实现、状态,以及你想限制继承的类数量时,应使用抽象类。
// 定义接口
interface IMyInterface()
{
void WriteLine();
}

// 继承类
class MyWriteSome : IMyInterface()
{
public void WriteLine()
{
Console.WriteLine("Hello World!");
}
}
using System;

// 定义动物接口
public interface IAnimal
{
void MakeSound();
}

// 实现动物接口的基础抽象类
public abstract class Animal : IAnimal
{
public abstract void MakeSound();
}

// 鸟类继承自动物抽象类并实现其方法
public class Bird : Animal
{
public override void MakeSound()
{
Console.WriteLine("Bird is chirping.");
}
}

// 狗类继承自动物抽象类但没有实现接口的方法
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Dog is barking.");
}
}

class Program
{
static void Main(string[] args)
{
// 创建鸟类对象
Bird bird = new Bird();
bird.MakeSound(); // 输出:Bird is chirping.

// 创建狗类对象
Dog dog = new Dog();
dog.MakeSound(); // 输出:Dog is barking.

// 使用接口引用不同类型的动物对象
IAnimal animal1 = new Bird();
animal1.MakeSound(); // 输出:Bird is chirping.

IAnimal animal2 = new Dog();
animal2.MakeSound(); // 输出:Dog is barking.
}
}

3 命名空间

定义:命名空间是C#中用于组织代码和防止命名冲突的重要机制。它允许开发者将相关的类型(类、接口、枚举、委托等)分组到一个逻辑容器中,这有助于大型项目的管理和维护。

用途:

  1. 代码组织:将相关的类型组织在一起,提高代码的可读性和可管理性。
  2. 避免命名冲突:不同的命名空间可以使用相同的类型名,这在大型项目或使用多个外部库时尤为重要。
  3. 访问控制:命名空间可以限制对特定类型或成员的访问,例如,使用 internal 访问修饰符可以使类型只在同一个解决方案内可访问。

声明命名空间:在C#中,命名空间使用关键字 namespace 来声明。命名空间的名称通常遵循反向域名的约定,例如 com.example.project

使用命名空间:在C#中,可以使用 using 指令在当前文件中导入其他命名空间中的类型,这样就可以直接使用类型名,而无需完整命名空间限定。

在C#中,命名空间可以嵌套,这意味着一个命名空间内部可以包含另一个命名空间。这在大型项目中非常有用,可以帮助进一步组织和分组代码。下面是一个示例,展示了嵌套命名空间的使用:

// 定义命名空间 com.example.mylib
namespace com.example.mylib
{
// 在命名空间中声明一个类
public class MyClass
{
public void SayHello()
{
Console.WriteLine("Hello from MyClass!");
}
}
}

// 定义另一个命名空间 com.example.client
namespace com.example.client
{
using com.example.mylib; // 导入命名空间中的类型

// 在这个命名空间中声明一个类
public class ClientClass
{
public void UseMyClass()
{
MyClass mc = new MyClass(); // 直接使用导入的类型
mc.SayHello();
}
}
}
// 命名空间嵌套案例
// 定义根命名空间 com.example.mylib
namespace com.example.mylib
{
// 定义子命名空间 sublib
namespace sublib
{
// 在子命名空间中声明一个类
public class SubClass
{
public void SayHello()
{
Console.WriteLine("Hello from SubClass!");
}
}
}

// 在根命名空间中声明另一个类
public class MyClass
{
public void SayHello()
{
Console.WriteLine("Hello from MyClass!");
}
}
}

// 定义另一个命名空间 com.example.client
namespace com.example.client
{
using com.example.mylib; // 导入命名空间中的类型
using com.example.mylib.sublib; // 导入嵌套命名空间中的类型

// 在这个命名空间中声明一个类
public class ClientClass
{
public void UseMyClass()
{
MyClass mc = new MyClass(); // 直接使用导入的类型
mc.SayHello();

SubClass sc = new SubClass(); // 直接使用嵌套命名空间中的类型
sc.SayHello();
}
}
}

4 反射

定义:反射是.NET Framework中一项强大的特性,它允许程序在运行时检查和操作类型信息,包括类、方法、属性、事件等。反射可以用于动态创建对象、调用方法、读写字段或属性值,以及获取和操作元数据,如类型名称、命名空间、程序集信息等。这一特性在实现诸如动态配置、插件架构、元编程、框架和库开发、单元测试等方面非常有用。

程序集是.NET Framework中代码的物理单元,它包含了编译后的类型、资源和其他元数据。一个程序集可以是一个单独的DLL或EXE文件,也可以是一组多个文件组成的复合程序集。

Assembly 对象提供了关于程序集的详细信息,包括程序集的名称、版本、文化信息、公钥令牌、包含的类型和资源等。通过 Assembly 对象,你可以使用反射来动态地加载和访问程序集中的类型和成员。

// 通过反射观察类中的字段
namespace MyReflection
{
internal class MyClass
{
private int a;
private int b;
public int c;
public string d;

public string Name { get; set;}
public string Sex { get; set; }

public void FuncOne() {}
public void FuncTwo() {}
}
}

namespace MyReflection
{
internal class Program
{
static void Main(string[] args)
{
// 获取某个类的类型(class)
Type t = typeof(MyClass);

// 也可以通过实例获取
// MyClass myClass = new MyClass();
// Type t = myClass.GetType();

// 类名(MyClass)
Console.WriteLine(t.Name);
// 命名空间名字(MyReflection)
Console.WriteLine(t.Namespace);
// 获得程序集
Console.WriteLine(t.Assembly);
// 获得所有字段(输出cd,因为ab是私有的)
FieldInfo[] fis = t.GetFields();
foreach(FieldInfo fi in fis)
{
Console.WriteLine(fi.Name);
}
// 获得所有属性(输出Name和Sex)
PropertyInfo[] pis = t.GetProperty();
foreach(FieldInfo pi in pis)
{
Console.WriteLine(pi.Name);
}
// 获得所有方法(输出FuncOne和FuncTwo,以及GetType,ToString,Equals,GetHashCode,因为父类(object)里面有这些方法,所以会额外多出几个方法)
MethodInfo[] mis = t.GetMethod();
foreach(FieldInfo mi in mis)
{
Console.WriteLine(mi.Name);
}
}
}
}

*5 特性

定义:特性是一种允许我们向程序的程序集增加元数据的语言结构。它是用于保存程序结构信息的某种特殊类型的类。简单来说就是为某些数据额外增加一些信息。

// 系统特性
// [Obsolete]
using System;
namespace MyAttribute
{
internal class Program
{
// 特性:弃用(参数:提示语,是否报废(true:使用直接报错,false:报警告(默认)))
[Obsolete("这个方法弃用了,请使用NewTest方法", false)]
static void Test()
{
Console.WriteLine("test");
}

static void NewTest()
{
Console.WriteLine("NewTest");
}

static void Main(string[] args)
{
// 可以调用,但是会报警告
Test();
}
}
}


// [Conditional]
// 定义了宏,方法才会执行,将宏注释后方法将不会执行
#define IsShowMessage

using System;
using System.Diagnostics;
namespace MyAttribute
{
internal class Program
{
// 是否调用(参数:宏名(宏存在就被调用,否则不调用))
[Conditional("IsShowMessage")]
static DebugShowMessage(string str)
{
Console.WriteLine(str);
}

static void Main(string[] args)
{
DebugShowMessage("Start of test");

Console.WriteLine("Doing work in main");

DebugShowMessage("End of test");
}
}
}
// 定义特性
public class HeroAttribute: Attribute
{

}

public class

6 索引器

写在类中,类似于重载了类的索引。

索引器也可以进行重载,重载通过输入参数和返回值进行区分。使用时根据输入的参数不同来使用不同的索引器。

// 语法:element-type this[int index]
namespace IndexerApplication
{
   class IndexedNames
   {
      private string[] namelist = new string[size];
      static public int size = 10;
      public IndexedNames()
      {
         for (int i = 0; i < size; i++)
         namelist[i] = "N. A.";
      }
// 进行索引器设置
      public string this[int index]
      {
         get
         {
            string tmp;

            if( index >= 0 && index <= size-1 )
            {
               tmp = namelist[index];
            }
            else
            {
               tmp = "";
            }

            return ( tmp );
         }
         set
         {
            if( index >= 0 && index <= size-1 )
            {
               namelist[index] = value;
            }
         }
      }
// 重载索引器
public int this[string name] {}

      static void Main(string[] args)
      {
         IndexedNames names = new IndexedNames();
         names[0] = "Zara";
         names[1] = "Riz";
         names[2] = "Nuha";
         names[3] = "Asif";
         names[4] = "Davinder";
         names[5] = "Sunil";
         names[6] = "Rubic";
         for ( int i = 0; i < IndexedNames.size; i++ )
         {
            Console.WriteLine(names[i]);
         }
         Console.ReadKey();
      }
   }
}

7 委托

委托是存有对某个方法的引用的一种引用类型变量,用于实现事件和回调方法,所有的委托都派生自System.Delegate类。

委托相当于C++的函数指针。

委托对象可以通过加减合并,形成调用列表。这称为委托的多播。内部数据结构是一个队列,先进先出。

// 声明,语法:delegate <return type> <delegate-name> (parameter list)
namespace Test1
{
class Test
{
// 定义委托(委托带有参数)
delegate int NumberChanger(int n);

static int num = 10;
// 定义用于委托的函数(参数要和委托一致)
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}

static void Main(string[] args)
{
// 创建委托实例(绑定对应函数)
NumberChanger nc = new NumberChanger(AddNum);
// 使用委托对象调用方法
nc(25);
Console.WriteLine("Value of Num: {0}", getNum());
// 修改委托指向的函数并调用
nc = MultNum;
nc(5);
Console.WriteLine("Value of Num: {0}", getNum());
}
}
}

// 委托的多播
static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
// 配置多播
nc = nc1;
nc += nc2;
// 调用多播
nc(5);
// 输出75,委托按顺序全部执行了。
Console.WriteLine("Value of Num: {0}", getNum());
}
// 常用的内置委托类型:Action和Func。Action无返回值,Func有返回值。
// 1.使用Action定义一个不带参数的方法
Action sayHello = () => Console.WriteLine("Hello!");
sayHello();

// 2.使用Action定义一个带有参数的方法
Action<string> sayHelloTo = (name) => Console.WriteLine("Hello, " + name + "!");
sayHelloTo("John");

// 3.使用Func定义一个带有参数和返回值的方法,最后一个泛型是返回值
Func<int, int, int> add = (a, b) => a + b;
int result = add(3, 5);
Console.WriteLine(result); // 输出:8

8 事件

发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。

// 简单的监听事件
public class Demo
{
private int myValue;

public int MyValue
{
get { return myValue; }
set
{
myValue = value;
// 非空则触发事件
MyValueChanged?.Invoke();
}
}
// 新建事件
public event Action MyValueChanged;
// 事件被触发的函数
public void OnMyValueChanged()
{
Console.WriteLine("Changed!");
}
}
public class Program
{
public static void Main(string[] args)
{
Demo demo = new Demo();
// 绑定时间的触发函数
demo.MyValueChanged += demo.OnMyValueChanged;

// 调用MyValue的set方法,触发事件
demo.MyValue = 10;
demo.MyValue = 10;
demo.MyValue = 10;
}
}

// 预定义的委托事件
public class MyClass
{
public event EventHandler MyEvent;

public void OnMyEvent()
{
MyEvent?.Invoke(this, EventArgs.Empty);
}
}

public class Program
{
public static void Main(string[] args)
{
MyClass myObject = new MyClass();
myObject.MyEvent += MyEventHandler;

myObject.OnMyEvent();
}
// sender:表示触发事件的对象,通常是引发事件的对象实例。
// e:是一个包含事件数据的类,它派生自System.EventArgs类。它可以携带与事件相关的信息,例如事件的名称、时间戳或其他自定义数据。
private static void MyEventHandler(object sender, EventArgs e)
{
Console.WriteLine("Event handled!");
}
}

9 泛型

泛用类型,T可以是不同的数据类型。

约束类型 作用
where T: struct T必须是一个值类型
where T : class T必须是一个引用类型
where T : new() T必须有一个无参数的公共构造函数
where T : <base class name> T必须是指定的基类或派生自指定的基类
where T : <interface name> T必须实现指定的接口
where T : U T必须是U或派生自U
where T : notnull T不能为null
where T : unmanaged T必须是非托管类型
// 函数参数泛型
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}

// 双泛型类
public class GenericClass<T1, T2>
{
private T1 genericMember1;
private T2 genericMember2;

// 构造函数
public GenericClass(T1 value1, T2 value2)
{
genericMember1 = value1;
genericMember2 = value2;
}
}

// 泛型委托
delegate T NumberChanger<T>(T n);

// 在委托后面可以接where T: ...来约束类型
public void Request<T>() where T : class

10 匿名方法

没有名字的方法。例如:lambda表达式。

// 结合委托,直接定义内容,不定义方法名
using System;

class Program
{
delegate int ArithmeticOperation(int x, int y);

static void Main()
{
// 使用匿名方法定义一个加法操作
ArithmeticOperation addition = delegate (int x, int y)
{
return x + y;
};

// 使用匿名方法定义一个乘法操作
ArithmeticOperation multiplication = delegate (int x, int y)
{
return x * y;
};

// 执行加法和乘法操作
int resultAdd = addition(3, 5);
int resultMultiply = multiplication(3, 5);

Console.WriteLine($"Addition result: {resultAdd}");
Console.WriteLine($"Multiplication result: {resultMultiply}");
}
}


// lambda 表达式
using System;

class Program
{
delegate int ArithmeticOperation(int x, int y);

static void Main()
{
int a = 5;
Func<int, int> f = x => x + a;
Console.WriteLine(f(4));
a = 8;
Console.WriteLine(f(4));
}
}

*11 多线程