C#基础

发布于 4 天前  0 次阅读



本文章基于 BV1Ew411o73C 记录

变量命名规则

变量命名规范

  • 不能使用关键字
  • 头第一个字母只能是 _ 或者字符,其他位置可以是 _ 、数字或 字母
  • 可以使用中文
  • 警告: 不要用中文来命名变量或函数,可能出现奇怪的编译错误

项目命名规范

  • 可以有个人的喜欢,但请务整个项目使用同一种命名规范
  • 变量名: 小写开头,小驼峰
  • 函数名,类名 大写开头,大驼峰

存储单位

变量定义的方式: TypeName varName = initVal;

int 大小:4B 范围: -2,147,483,648 到 2,147,483,647 ,有符号整数,可以是负数

int valInt = -5;

uint 大小:4B 范围: 0 到 4,294,967,295 ,无符号整数 0 和正整数 ,不能表示负数

float 大小:4B 范围: ±1.5 x 10−45 至 ±3.4 x 1038 精度:大约 6-9 位数字

float valFloat = 3.14f;

double 大小:8B 范围:精度: ±5.0 × 10−324 到 ±1.7 × 10308 精度: 大约 15-17 位数字

bool 由编译器决定:值为 true 或 false

char 2B,存储单个字符, 如 ‘a’ ‘中’

char valChar = ‘s’;

string 存储字符串,由多个char 组成

string valStr = “Hello,中国”;

T[] 数组,可以存储多个T 类型的数据

  • 定义一个int型的数组变量 array, 该变量里面可以存储十个int型数据大小的数据
  • 访问和设置的方式: ary[0] = 3; int intVal = ary[3];
  • Csharp 中的下标是从 0 开始的
  • 长度为 ary.Length
int[] ary = new int[10];

默认值&类型转化

数值型:int, float:0

Bool:false

引用型:null:string, int[]

类型转化

bool va1Bool = false;
float va1Float = 3.14f;
double va1Double = 6.28;
char ch1 = 'a';
char ch2 = '叶';
string ca1Str = "Hello,中国";
string va1StrNull = null;

byte va1Byte = 255; //0~255
//byte va1Byte2 = -1; //无符号不能表示负数
sbyte va1SByte = -1;
short va1Short = 44;
int va1Int = 55445;
long va1Long = 12345678910111213;

int[] intAry = new int[10];
string[] strAry1 = null;
string[] strAry2 = new string[3];
strAry2[2] = null;

//vaDoble = 6.28
float va1Float2 = (float)va1Double; //显示类型转换
double va1Double2 = va1Float; //隐式类型转换
int va1Int2 = (int)va1Float; // valInt2 = 6;

string va1Str2 = "5";
// 字符串 数值型 互转
int va1Int3 = int.Parse(va1Str2);
long va1Long3 = long.Parse(va1Str2);
string va1Str3 = va1Long.ToString();

自增 & 字符串格式化

+ - * / 数学运算 加减乘除

  • float val = i+ 3* 4.5f;

自增自减 i++;i—–;

  • ++1// i = i + 1; return i;
  • i++; // int r =i; i = i + 1; return r;

+= -=

  • a += b;// 等价于 a = a + b;
  • a -= b;// 等价于 a = a - b;

字符串格式化 $"info = {a}"

int result = 3;
string inputStr = "va1";
string str1 = inputStr + "的计算结果为:" + result;
string str2 = $"{inputStr}的计算结果为:{result}";

函数

返回值类型 函数名(参数列表){ 任意条指令 ; return 返回值;}

Bool

Bool 比较

  • < 小于
  • <= 小于等于
  • >大于
  • />= 大于等于
  • = 是否相等
  • != 是否不等

Bool 组合

  • 非: ! 对一个值取反 !true == false;!false == true;!!false = false
  • 且:a && b 两边都为 true 时候,才返回 true,否则返回 false 当左表达式为 false,直接返回 false,右边表达式不会进行求值
  • 或:a || b 两边都为 false 时候,才返回 false,否则返回 true 当左表达式为 true,直接返回 true,右边表达式不会进行求值

条件语句

if (gender == "男") 
{
    title = "帅哥";
} 
else if (gender = "女")
{
    title = "美女";
} 
else 
{
    title = "朋友";
}
Console.WriteLine($"这位{title},欢迎光临");

if 语句注意事项

代码可以单行

没有大括号,if 语句块只负责一条语句(不是一行)或一个语句块

Switch

string gender = Console.ReadLine();
bool isOther = false;
switch (gender) 
{
    case "男": title = "帅哥"; break;
    case "女": title = "美女"; break;
    default: title = "朋友"; isOther = true; break;
}

Enum

Enum 枚举 : 用于代替常量(魔数),增加代码可读性

Enum 可转为 int, 也可转为 string , 可以互相转换

enum EGenderType
{
    Male = 0,
    Female, // == 1
    Other = 3
}

循环

while

while (condition) {/* 任意代码*/} }

当条件 condition 为 true 的时候 会一直执行{}中的代码

int i = 0;
while (i < 3)
{
    Console.WriteLine($"i={i}");
    i++;
}

Continue

continue 会停止当前代码块的执行,让循环,进入下一个条件判定中

int i = 0;
while (i < 4)
{
    i++;
    if (i == 2) 
    {
        continue;
    }
    Console.WriteLine("i = " + i);
}

Break

break 终止当前循环,执行循环语句后的下一条语句

int i = 0;
while (i < 5)
{
    i++;
    if (i == 3) 
    {
        break;
    }
    Console.WriteLine("i={i}");
}
Console.WriteLine("End of While");

DoWhile

do {/* 任意代码*/ } while(condition); //先执行代码,然后再判定条件 如果条件满足会继续执行

while (false) 
{
    // 任意条语句
    Console.WriteLine("While false");
}
do 
{
    // 任意条语句
    Console.WriteLine("Do While false");
}
while (false);
int i = 0;
do 
{
    Console.WriteLine("i = " + i);
    i++;
} while (i < 3);

For

for(初始化语句;条件判定;末尾语句){/* 任意代码*/}

可以理解为有初始化语句和末尾语句的 while

初始化语句有且仅执行一次

末尾语句是代码快执行完之后再执行

循环:总结

while (condition) {/* 任意代码*/} } //当条件满足的时候 会一直执行

do {/* 任意代码*/} } while(condition); //先执行代码,然后再判定条件 如果条件满足会继续执行

for(初始化语句;条件判定;末尾语句){{/* 任意代码*/} //可以理解为有初始化语句和末尾语句的 while

continue, break // 跳过语句块,进入循环的下一环节, break 直接终止当前循环

class 类型名字 { /* 定义*/}

class Human
{
    int age;
    string name;
}

类型的存储大小由其成员变量来确定

Human.MaxAge = 100;
int humanMaxAge = Human.MaxAge;

实例创建的方式 ,默认值是 null

Human human = new Human; // 实例 对象

实例成员变量的访问方式

human.name = "Name"; //报错

访问级别:public, private

类型里面定义静态函数(访问级别类似)

类型里面定义静态变量

class Human
{
    public const int MaxAge = 200;
    public static bol canFly = False;
    int age;
    public string name;
    public static void PrintTypeName()
    {
        Console.WriteLine("TypeName = Human");
    }
}
string myName = human.name;
Human.PrintTypeName();

静态变量的访问方式

Human canFly = true;
bool canFly = Human.canFly;

const 常量的定义和访问方式

成员函数的定义和访问

partial class Human
{
    public const int MaxAge = 200;
    public static bool canFly = false;
    int age;
    public string name;
    public void SetAge (int value)
    {
        this.age = value;
    }
    public void SetAge (Human _this, int value) 
    {
        _this.age = value;
    }
    public static void PrintTypeName ()
    {
        Log("TypeName = Human");
    }
}

Human human = new Human();  // 实例 对象
human.SetAge(17);
Human.SetAge(human, 20);

使用静态函数来模拟成员函数

partial 让类的定义放在多个地方

属性 Property:看起来像变量的函数

partial class Human
{
    public int Age
    {
        get 
        {
            Log("getAge{age}");
                return age;
        }        set        {
            Log("setAge{age}=>{value}");
            age = value;
        }
    }
    public static bool CanFly
    {
        get => canFly;
        set => canFly = value;
   }
}

Human.CanFly = true;
bool staticCanFly = Human.CanFly;

human.Age = 32; // 属性读写
int myAge = human.Age;

构造函数

partial class Human
{
    public const int MaxAge = 200;
    public static bool canFly = false;
    int age;
    public string name;
    public Human ()
    {
        this.age = 17;
        name = "";
    }
    public Huamn(string name)
    {
        this.age = 17;
        this.name = name;
    }
    public void SetAge (int value)
    {
        this.age = value;
    }
    public void SetAge (Human _this, int value)
   {
       _this.age = value;
   }
    public static void PrintTypeName ()
    {
        Log("TypeName = Human");
    }
}


Human human = new Human();
Human human2 = new Human("Cwlrin");

结构体

值类型 和 引用类型的区别

  • 结构体 是值类型,Class 是引用类型
  • 在函数调用内部指令中,struct 直接在栈中进行内存分配,class 是在堆中进行内存分配
  • 栈中的内存,会在函数调用完成后回收, 堆中的内存由csharp 运行时自动回收 (GC)
  • 在函数调用过程中,class 拷贝只是拷贝地址,struct 是所有数据都拷贝(深拷贝)
  • 如果需要将 struct 的引用传递给函数内部,则需要使用关键字 ref

内存分布

  • Readonly: 代码区,Const
  • 代码执行区域:stack,栈
  • 数据存储:heap
  • 全局区域:static,类型信息

值和引用的关系

Class 在创建实例(new)的时候,是在堆中分配内存,然后返回的是那片内存的地址

Struct 在创建实例 在栈中分配,引用变量存的是地址,值变量存的是直接的数据

如果想要让 Struct 也能被函数内的修改,使用 ref

partial struct SVector
{
    public float x;
    public float y;
    public void Print()
    {
        Log("SVec({x},{y})");
    }
}
partial struct CVector
{
    public float x;
    public float y;
    public void Print()
    {
        Log("SVec({x},{y})");
    }
}
static void TestStruct (CVector cVec, SVector sVec)
{
    cVec.x = 100;
    sVec.x = 100;
    cVec.Print();
    sVec.Print();
}
static void TestStruct (CVector cVec, ref SVector sVec)
{
    cVec.x = 100;
    sVec.x = 100;
    cVec.Print();
    sVec.Print();
}
cVec = new Cvector();
sVec = new SVector();
TestStruct(cVec, sVec);
Console.WriteLine("after call");
cVec.Print();
sVec.Print();
Console.WriteLine("----- Reference -----");
cVec = new Cvector();
sVec = new SVector();
TestStruct(cVec, ref sVec);
Console.WriteLine("after call");
cVec.Print();
sVec.Print();
/**
 * CVec (100, 0)
 * SVec (100, 0)
 * after all
 * CVec (100, 0)
 * SVec (0,0)
 * ----- Reference -----
 * CVec (100, 0)
 * SVec (100, 0)
 * after all
 * CVec (100, 0)
 * SVec (100, 0)
 */

继承

概念 : Class B : A {} 类型 B 继承自类型 A

A 叫做父类,B 叫做子类

B 从 A 中继承:属性和方法

Override: 给个机会给子类去实现

Protected:子类能访问,外面不能访问

public abstract partial class Actor  
{
    public string name;

    public int health;
    public int damage;
    // public 所有人都能访问
    public string skill;
    // protected 只能自己和子类访问
    protected int power;
    // private 只能自身内部方法可以访问
    private int money;
    public virtual void Attack()
    {
        power -= 10          
        Console.WritLine($"{name} 发动技能 {skill}");
    }
}
public partial class Knight : Actor
{
    public override void Attack()   
    {
        power -= 30;   
    }
    public void MyFunc()
    {
       this.damage = 10;
       this.Attack();
    }
}
public partial class Mage : Actor
{
    public override void Update()
    {
        power += 2;
        Print();
    }
    public override void Awake () { }
    public void Destroy() { }
}

Knight warrior = new Knight();
warrior.name = "骑士";
warrior.skill = "冲锋";
warrior.Attack();

Mage mage = new Mage();
mage.name = "法师";
mage.skill = "火球术";
mage.Attack();
mage.power = 10;

Struct 默认继承自object, class 如果没有明确指出继承自某个类型,那么其默认继承自object

Struct 不能继承 struct 或者class,但可以继承接口

pubic interface IAwake
{
    void Awake();
}
struct SVec : IAwake
{
    public int x;
    public void Awake() {}
}

Interface:声明我是一个什么样子的类型,不实现

public interface IUpdate
{
    void Update();
}
public interface ILifeCycle : IUpdate, IAwake { }

Class 只能继承一个或零个 基类, 但能继承任意多个接口

public class Mage : Actor, ILifeCycle, IDestroy
{
    public override void Update ()
   {
        power += 2;
        Print();
    }
    public override void Awake() {}
    public void Destroy () {}
}

Abstrct: 不能实例化,可以暂时不实现接口,等待子类的实现

public abstract partial calss Actor : ILifeCycle
{
    public void Update()
    {
        power += 1;
    }
    public abstract void Awake();
}

父类变量 可以存储 子类的变量(地址),但不能反过来

  • 潜在意思,因为子类 B 是 一个父类 A
  • 比如 A 是Human, B 是 Teacher, 学生是人,但人不一定都是老师
Actor baseActor = new Monster();
baseActor.Update();
// baseActor.Print(); protected 外面不能访问
// Monster baseActor = new Actor();
// 子类不能持有父类,Actor 不是一个 Monster
ILifeCycle updater = monster;
update.Update();

Object

所有类型的基类 System.Object

ToString() 函数可以重写

泛型

泛型的定义

public class GVector<T>
{
    public T x;
    public T y;
    public GVector(T x, T y)
   {
        this.x = x;
        this.y = y;
    }       
    public void Print () { ... }
}

GVector<int> giv = new GVector<int>(0, 1); 
GVector<float> gfv = new GVector<float>(0, 1.5f);
GVector<string> gsv = new GVector<string>("", "1.5f");

泛型的约束

  • new() 要求有默认构造函数,可以调用new T()
  • class 要求是 class,可以设置为 null

容器

List

List 动态数组,自动增长

private static void TestList()
{
    var lst = new List<int>() { 3, 4, 5 };
    PrintCollections(lst, "init list");
    lst.Add(8); //在末尾增加 
    PrintCollections(lst, "list add");    
    lst.Insert(2, 9); // 在下标 2 中插入 9
    PrintCollections(lst, "list insert");
    lst.RemoveAt(2); // 移除下标 2 中的元素
    PrintCollections(lst, "list RemoveAt");
    lst[2] = 15; // 修改下标 2 中的内容
    PrintCollection(lst, "list []");
    lst.Sort(); // 排序
    PrintCollections(lst, "list sort");
}
static void PrintCollections<T>(IEnumerable<T> lst, string msg = "")
{
    Console.WriteLine($"-----{msg} count = {lst.Count()} -----");
    foreach (var item in lst)
    {
        Console.Write(item.ToString() + "");
    }
    Console.WriteLine();
}
/**
* ----- init list count = 3 -----
* 3 4 5
* ----- list add count = 4 -----
* 3 4 5 8
* ----- list insert count = 5 -----
* 3 4 9 5 8
* ----- list RemoveAt count = 4 -----
* 3 4 5 8
* ----- list [] count = 4 -----
* 3 4 15 8
* ----- list sort count = 4 -----
* 3 4 8 15
*/

Dictionary

Dictionary:使用 Key 来获取 Value

var dict = new Dictionary<int, string> ()
{
    { 3, "_3" },
    { 5, "_5" },
};
PrintCollections(dict, "init Dict");
dict.Add(8, "_8"); // 插入对应的元素
PrintCollections(dict, "dict add");
// 如果不存在则插入对应的元素,存在的话则修改 key = 2 对应的内容为 "_2"
dict[2] = "_2";
PrintCollections(dict, "dict []");
dict[2] = "_@2";
// 如果存在 key = 2,则去除对应的值,放到变量 val 中,并返回 true,否则返回 false
if (dict.TryGetValue(2, out string val))
{
    PrintCollections(dict, "dict TryGetValue succ "+ val);
}
// 查询是否存在 Key = 33
PrintCollections(dict, "dict ContainsKey 33 = " + dict.ContaisKey(33));
// 尝试移除 Key = 8 元素,如果之前存在,返回 true,否则返回 false
var isSucc8 = dict.Remove(8);
PrintCollections(dict, "dict Remove8" + isSucc8);
var isSucc7 = dict.Remove(7);
PrintCollections(dict, "dict Remove7" + isSucc7);
// 获取所有的 Key
PrintCollections(dice.Keys, "dictKeys");
// 获取所有的 Value
PrintCollections(dict.Values, "dict Values");
static void PrintCollections<TKey, TVal>(Dictionary<TKey, TVal> dict, string msg = "")
{
    Console.WriteLine("----- {msg} -----");
    foreach (KeyValuePair<TKey, TVal> item in dict)
    {
        Console.Write("({item.Key} = {item.value})");
    }
    Console.WriteLine();
}
/**
* ----- init Dict -----
* (3 = _3) (5 = _5)
* ----- dict add -----
* (3 = _3) (5 = _5) (8 = _8)
* ----- dict [] -----
* (3 = _3) (5 = _5) (8 = _8) (2 = _2)
* ----- dict TryGetValue succ _@2 -----
* (3 = _3) (5 = _5) (8 = _8) (2 = _@2)
* ----- dict ContainsKey 33 = False -----
* (3 = _3) (5 = _5) (8 = _8) (2 = _@2)
* ----- dict Remove8True -----
* (3 = _3) (5 = _5) (2 = _@2)
* ----- dict Remove7False -----
* (3 = _3) (5 = _5) (2 = _@2)
* ----- dict Keys count = 3 -----
* 3 5 2
* ----- dict Values count = 3 ----- 
* _3 _5 _@2
*/

HashSet

HashSet 保证里面的元素是唯一的

var set = new HashSet<int>() { 3, 4, 5 };
PrintCollections(set, "init HashSet");
PrintCollections(set, "init HashSet Add 8 =" + set.Add(8));PrintCollections(set, "init HashSet Add 8 = " + set.Add(8));
PrintCollections(set, "init HashSet Remove2 = " + set.Remove(3));
PrintCollections(set, "init HashSet Contains 8 = " + set.Contains(8));
static void PrintCollections<T>(IEnumerable<T> lst, string msg ="")
{
    Console.WriteLine($"----- {msg} count = {lst.Count()} -----");      
    foreach (var item in lst)
    {
        Console.Write(item.ToString() + " ");
    }
    Console.WriteLine();
}
/**
* ----- init HashSet count = 3 -----
* 3 4 5
* ----- HashSet Add 8 = True count = 4 -----
* 3 4 5 8
* ----- HashSet Add 8 = False count = 4 -----
* 3 4 5 8
* ----- HashSet Remove2 = True count = 3 -----
* 4 5 8
* ----- HashSet Contains 8 = True count = 3 -----
* 4 5 8
*/

Queue

Queue 可动态增长先进先出

// 先进先出
var strack = new Queue<int>();
stack.Enqueue(1);
stack.Enqueue(2);
stack.Enqueue(3);
stack.Enqueue(4);
PrintCollections(stack, "init Queue");
stack.Enqueue(8);
PrintCollections(stack, "Queue Push 8");
PrintCollections(stack, "Queue Pop" + stack.Dequeue());
stack.Enqueue(9);
PrintCollections(stack, "Queue Push9");
PrintCollections(stack, "Queue Pop" + stack.Dequeue());
PrintCollections(stack, "Queue Pop" + stack.Dequeue());
PrintCollections(stack, "Queue count = " + stack.Count());
static void PrintCollections<T>(IEnumerable<T> lst, string msg ="")
{
    Console.WriteLine($"----- {msg} count = {lst.Count()} -----");
    foreach (var item in lst)
   {
        Console.Write(item.ToString() + " ");
    }
    Console.WriteLine();
}
/**
* ----- init Queue count = 4 -----
* 1 2 3 4
* ----- Queue Push 8 count = 5 -----
* 1 2 3 4 8
* ----- Queue Pop 1 count = 4 -----  
* 2 3 4 8
* ----- Queue Push 9 count = 5 -----
* 2 3 4 8 9
* ----- Queue Pop = 2 count = 4 -----
* 3 4 8 9
* ----- Queue Pop = 3 count = 3 -----
* 4 8 9
* ----- Queue count = 3 count = 3 -----
* 4 8 9
*/

Stack

Stack 可动态增长后进先出

// 后进先出
var strack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack.Push(4);
PrintCollections(stack, "init Stack");
stack.Push(8);
PrintCollections(stack, "Stack Push 8");
PrintCollections(stack, "Stack Pop" + stack.Pop());
stack.Enqueue(9);
PrintCollections(stack, "Stack Push9");
PrintCollections(stack, "Stack Pop" + stack.Pop());
PrintCollections(stack, "Stack Pop" + stack.Pop());
PrintCollections(stack, "Stack count = " + stack.Count());
static void PrintCollections<T>(IEnumerable<T> lst, string msg ="")
{
    Console.WriteLine($"----- {msg} count = {lst.Count()} -----");
    foreach (var item in lst)
    {
        Console.Write(item.ToString() + " ")           
    }
    Console.WriteLine();
}
/**
* ----- init Stack count = 4 -----
* 4 3 2 1
* ----- Stack Push 8 count = 5 -----
* 8 4 3 2 1
* ----- Stack Pop 8 count = 4 -----
* 4 3 2 1
* ----- Stack Push 9 count = 5 -----
* 9 4 3 2 1
* ----- Stack Pop = 9 count = 4 -----
* 4 3 2 1
* ----- Stack Pop = 4 count = 3 -----
* 3 2 1
* ----- Stack count = 3 count = 3 -----
* 3 2 1
*/

函数指针

delegate :可以保存多个函数指针,使用 += -= 进行操作

public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);

匿名函数:方便代码的编写,编译器会给他取名字

public delegate void Action<in T>(T obj);

Action :无返回值的 delegate

Func:有返回值的 delegate 监听者模式

监听者模式

delegate float CalcFunc(float val1, float val2);
static float Add(float val1, float val2)
{
    Console.WriteLine("{val1} + {val2} = {val1 + val2}");
    return val1 + val2;
}
static float Sub(float val1, float val2)
{
    Console.WriteLine("{val1} - {val2} = {val1 - val2}");
    return val1 - val2;
}
static float Mul(float val1, float val2)
{
    Console.WriteLine($"{val1} * {val2} = {val1 * val2}");
    return val1 * val2;
}
static void Print(string info) => Console.WriteLine(info);

Console.WriteLine("----- Action 无返回值 -----");
Action<string> print = Print;
print("Hello world");
Console.WriteLine("----- Func 可以有返回值 -----");
Func<float, float, float> func = Mul;
print(func(3, 4).ToString());

Console.WriteLine("----- Init Add -----");
CalcFunc listenerFunc = Add;
listenerFunc(3, 5.6f);
Console.WriteLine("----- After += Sub -----");
listenerFunc += Sub;
listenerFunc(3, 5.6f);

Console.WriteLine("----- After -= Sub -----");
listenerFunc -= Sub;
listenerFunc(3, 5.6f);

Console.WriteLine("----- After += Mul -----");
listenerFunc += Mul;
listenerFunc(3, 5.6f);

Console.WriteLine("----- 闭包 可以抓取上下文信息 -----");
int intVal = 10;
Action closure = () =>
{
    intVal += 10;
}
closure;
Console.WriteLine("closuer" + intVal);

/**
 * ----- Action 无返回值 -----
 * Hello world
 * ----- Func 可以有返回值 -----
 * 3 * 4 = 12
 * 12
 * ----- Init Add -----
 * 3 + 5.6 = 8.6
 * ----- After += Sub -----
 * 3 + 5.6 = 8.6
 * 3 - 5.6 = -2.6
 * ----- After -= Sub -----
 * 3 + 5.6 = 8.6
 * ----- After += Mul -----
 * 3 + 5.6 = 8.6
 * 3 * 5.6 = 16.8
 * ----- 闭包 可以抓取上下文信息 -----
 * closure 20
 */

属性 Attribute

Attribution :可以给函数,变量,类型等添加额外的用户自定义的信息,方便对语言拓展

配合反射来用

[AttributeUsage(AttributeTargets.All)]
public class InitCountAttribute : Attribute
{
    public int Value;
}
[InitCount(Value = 1)]
public class Player : Actor
{
    [InitCount(Value = 3)]
    ......
}

反射

创建实例

private static void TestReflection()
{
    object val = 0;
    val.GetType();
    var allTypes = typeof(Program).Assembly.GetTypes();
    foreach (var type in allTypes)
    {
        Console.WriteLine(type.Namespace + "." + type.Name);
        var attributes = type.GetCustomAttributes(typeof(InitCountAttribute), true);
        if(attributes.Length > 0)
        {
            PrintTypeInfo(type);
        }
    }
    PrintTypes(allTypes);
}
private static void PrintTypes(Type[] allTypes)
{
    Console.WriteLine("----- CreateObjects -----");
    List<Object> objs = new List<object>();
    foreach (var type in allTypes)
    {
        if (type.IsClass && !type.IsAbstract)
        {
            var obj = Activator.CreateInstance(type);
            objs.Add(obj);
        }
    }
    foreach (var item in objs)
    {
        Console.WriteLine(item.ToString());
    }
}
/**
 * ----- CreateObjects -----
 * Lec21_Reflection.Program
 * TGame.Enemy
 * TGame.InitCountAttribute
 * TGame.Player
 * TGame.GameManager
 * TGame.Time
 */

使用反射来注册信息,和生成代码(优化,适配)

编辑器 IDE 辅助查看,F12

#define 定义宏
#undef 取消宏定义
#if 判定是否定义了对应的宏
#else
#endif 结束标记

#define TEST_MACRO_WIN
#if TEST_MACRO_WIN
    #define ENABLE_LOG
#else 
    #undef ENABLE_LOG
#endif

using System;
public calss TestMacro
{
    public void ShowMacro()
    {
#if ENABLE_LOG
        Console.WriteLine("Log Something");
#else
        // Do something
        int val = 10;
#endif
    }
}

异常

代码执行过程中可能会抛出异常

  • NullReferenceException

不捕获将会导致程序退出结束

void TestException()
{
    try 
    {
        object obj = null;
        obj.ToString();
    }
    catch (NullReferenceException e || Exception e) 
    {
        Console.WriteLine(e.ToString());
    }
}

finally

  • throw;
  • throw new Exception(“Msg”);
  • try{} catch( Exception e){} finally{ /*一定执行的语句*/}
int val = 0;
try
{
    try
    {
        object obj = null;
        obj,ToString();
    }
    catch (InvalidCastException e)
    {
        throw;
    }
    finally
    {
        val = 10;
    }
    val = 5;
}
catch (Exception) { val++; }
Console.WriteLine(val);

操作符号重载

重载操作符可以让自定义类型像内置类型一样使用

符合用户的使用习惯

public struct Vector2
{
    public float x;
    public float y;
    public Vector2(float x, float y)
    {
        this.x = x;
        this.y = y;
    }
    public static Vector2 operator +(Vector2 a, Vector2 b) => new Vector2(a.x +b.x, a.y + b.y);
    public static Vector2 operator -(Vector2 a) => new Vector(-a.x, -a.y);
    public static Vector2 operator *(Vector2 a, float d) => new Vector2(a.x * d, a.y * d);
    ...
}

Vector2 vec21 = new Vector2(10, 3);
Vector2 vec22 = new Vector2(5, 3);
vec21 += vec22;
vec21 *= 3;
vec21 = -vec21;

相等判定重载后,需要重载

  • Equals
  • GetHashCode
public float this[int index]
{
    get
    {
        switch (index)
        {
            case 0: return this.x;
            case 1: return this.y;
            default: throw new IndexOutOfRangeException("Invalid Vector2 index!");
        }
    }
    set
    {
        switch (index)
        {
            case 0: this.x = value; break;
            case 1: this.y = value; break;
            default: throw new IndexOutOfRangeException("Invalid Vector2 index!");
        }
    }
}

public static bool operator == (Vector2 lhs, Vector2 rhs) => lhs == rhs;
public static bool operator != (Vector2 lhs, Vector2 rhs) => !(lhs == rhs);
public bool Equals(Vector2 other) => x == other.x && y == other.y;
public override int GetHashcode() => this.x.GetHashCode() ^ this.y.HashCode();
public override bool Equals(object other) => other is Vector2 other1 && Equal(Vector2 other);

vec21[0] = 3; // vec21.x = 3
var isSame = vec21 == vec22;

类型拓展

可以拓展一些无法修改源代码的类型

增加额外的接口,方便用户使用

必须是静态类,且必须是静态方法

函数参数前面使用 this 关键字

public static class TestFloatExt
{
    public static int ToInt(this float val)
    {
        return (int)val;
    }
}

float valFloat = 1.5f;
int valInt = valFloat.ToInt();

大变に气分がいい