# 变量命名规则
# 变量命名规范
- 不能使用 关键字
- 头第一个字母只能是
_
或者字符,其他位置可以是_
、数字或 字母 - 可以使用中文
- 警告: 不要用中文来命名变量或函数,可能出现奇怪的编译错误
# 项目命名规范
- 可以有个人的喜欢,但请务整个项目使用同一种命名规范
- 变量名: 小写开头,小驼峰
- 函数名,类名 大写开头,大驼峰
# 存储单位
变量定义的方式: 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(); |