本篇 核心知识点 :属性 Property、索引器 Indexer、反射 Reflection、类型判断 is/as、字符串安全转换 TryParse、反射实战(创建对象 / 调用方法 / 读取特性)、反射优缺点与项目使用规范
一、属性 Property
1. 概念
属性是类 / 结构体 / 接口的访问器,封装私有字段,对外提供统一读写入口,替代传统GetXxx()/SetXxx()函数,读写语法如同普通字段,但内部可附加校验、事件同步等业务逻辑。
2. 核心特性
-
拆分
get读取访问器、set写入访问器; -
set内置隐式关键字value,代表外部赋值传入的值; -
可做读写控制:仅写 get = 只读,仅写 set = 只写;
-
赋值、取值时自动触发内部代码(血量边界校验、UI 刷新、事件广播);
-
分为完整属性(配套私有字段)、自动实现属性(编译器自动生成隐藏字段)。
3. 完整手写属性(配套私有字段)
class Npc
{
// 私有字段,外部禁止直接访问
private int _hp;
private string _name;
// 血量属性:带边界校验
public int Hp
{
get { return _hp; }
set
{
// 血量约束 0 ~ 2000
if (value < 0) _hp = 0;
else if (value > 2000) _hp = 2000;
else _hp = value;
// 赋值后自动同步UI
Console.WriteLine("血量更新,刷新血条");
}
}
// 只读属性:只提供get,外部无法赋值
public string Name
{
get { return _name; }
}
// 构造初始化
public Npc(string name, int hp)
{
_name = name;
Hp = hp; // 走set自动校验
}
}
static void Main()
{
Np n = new N("僵尸", 3000);
Console.WriteLine(n.Hp); // 读取get,输出2000
n.Hp = -50;
Console.WriteLine(n.Hp); // 输出0
}
4. 自动实现属性(简化写法)
无需手动定义私有字段,编译器自动生成隐藏后备字段,适合无额外逻辑场景:
// 读写自动属性
public int Mp { get; set; }
// 只读自动属性(构造内赋值)
public int MaxAtk { get; }
5. 单例模式只读属性实战(饿汉)
class ResManager
{
// 私有静态实例
private static ResManager _inst = new ResManager();
// 只读对外属性,无set,外部不能修改
public static ResManager Instance
{
get { return _inst; }
}
// 私有构造,禁止外部new
private ResManager() { }
}
6. 抽象属性(抽象类专用)
抽象类中无实现的属性,派生类必须完整重写 get/set:
abstract class Person
{
public abstract string Name { get; set; }
}
class Player : Person
{
private string _n;
public override string Name
{
get { return _n; }
set { _n = value; }
}
}
拓展工程价值
游戏开发中金币、血量用属性赋值,可直接在 set 内写 UI 刷新、事件广播(观察者模式),无需每次修改后手动调用刷新函数。
二、索引器 Indexer
1. 概念
特殊的类访问器,使用this[]定义,让类对象可以像数组下标一样访问内部多组私有数据,本质是重载下标访问运算符。
2. 核心规则
-
固定语法
public 类型 this[索引参数] {get;set;}; -
索引参数支持 int/string 等任意类型;
-
一个类可重载多个索引器(参数类型 / 个数不同);
-
内部通过分支区分不同下标对应字段 / 数组。
3. 基础 int 索引器代码示例
class Npc
{
private int _hp = 1000;
private int _mp = 500;
// int下标索引器 0=血量 1=蓝量
public int this[int idx]
{
get
{
switch (idx)
{
case 0: return _hp;
case 1: return _mp;
default: return 0;
}
}
set
{
switch (idx)
{
case 0: _hp = value; break;
case 1: _mp = value; break;
}
}
}
}
static void Main()
{
Npc n = new Npc();
n[0] = 1500; // 修改hp
Console.WriteLine(n[1]); // 读取mp
}
4. 重载:string 字符串索引器(按名称取值)
// 重载字符串索引,通过字段名匹配
public int this(string key)
{
get
{
if (key == "hp") return _hp;
if (key == "mp") return _mp;
return -1;
}
}
5. 拓展实战场景
读取配置表、消息数据包时,循环通过索引批量赋值内部字段,简化大量重复赋值代码。
三、反射 Reflection
1. 核心概念
反射是 C# 运行时操作元数据的机制:程序运行期间,通过字符串类名 / 方法名,动态获取类型、构造、方法、属性,创建对象、调用函数、读取特性。
2. 反射核心元数据类(System.Reflection 命名空间)
| 类 | 作用 |
|---|---|
| Type | 存储类型全部元数据(类 / 结构体 / 接口) |
| ConstructorInfo | 构造函数信息 |
| MethodInfo | 普通方法信息 |
| PropertyInfo | 属性信息 |
| FieldInfo | 私有字段信息 |
| Attribute | 特性元数据读取载体 |
3. 获取 Type 三种方式
class Plant
{
public string Name { get; set; }
public int Hp;
public void ShowInfo() => Console.WriteLine(Name + " " + Hp);
}
static void Main()
{
// 方式1 类型直接获取
Type t1 = typeof(Plant);
// 方式2 通过实例对象获取
Plant p = new Plant();
Type t2 = p.GetType();
// 方式3 完整字符串类名(含命名空间)
Type t3 = Type.GetType("命名空间.Plant");
}
4. 反射创建对象(调用构造函数)
Type t = typeof(Plant);
// 1. 获取构造函数:参数类型数组
ConstructorInfo ctor = t.GetConstructor(new[] { typeof(string), typeof(int) });
// 2. 传入参数数组,实例化对象(object基类接收)
object obj = ctor.Invoke(new object[] { "向日葵", 100 });
// 3. 类型转换
Plant p = obj as Plant;
5. 反射调用类方法
// 获取ShowInfo方法元数据
MethodInfo method = t.GetMethod("ShowInfo");
// 调用,第一个参数为实例,无参数传null
method.Invoke(obj, null);
6. 反射读取 / 修改属性
PropertyInfo prop = t.GetProperty("Name");
// 读取
string name = prop.GetValue(obj).ToString();
// 修改
prop.SetValue(obj, "豌豆射手");
7. 反射读取自定义特性
依托前期 Attribute 特性,运行时通过 Type 获取标记的元数据备注、版本信息。
8. 反射优缺点
优点
-
完全动态:仅字符串即可创建对象、调用方法;
-
游戏热更新、插件系统、配置表序列化底层核心;
缺点
-
性能极差,元数据查找、Invoke 调用开销远高于直接代码;
-
破坏封装,可读取私有字段 / 方法;
9 项目使用规范
仅在初始化、资源加载一次性使用;禁止 Update / 循环内频繁反射调用,避免帧率暴跌。
四、类型安全判断与转换 is /as
1. is 关键字
概念:
判断对象是否属于目标类型,返回 bool,不会转换,无异常。
object obj = new Plant("坚果", 2000);
if(obj is Plant)
{
Console.WriteLine("该对象是植物类型");
}
2. as 安全转换
概念:
强制类型转换升级版,转换失败返回 null,不抛异常;仅引用类型可用。
object obj = new Plant("坚果", 2000);
Plant p = obj as Plant;
if(p != null) p.ShowInfo();
对比:直接强转(Plant)obj转换失败直接程序崩溃,生产优先as。
五、字符串安全转换 TryParse
1. 传统 Parse 缺陷
int.Parse("abc")格式错误直接抛异常,程序崩溃;
2. TryParse 安全转换(out 输出参数)
概念:
转换成功返回 true,失败返回 false,转换结果存入 out 变量,无崩溃风险。
string input = "123";
int num;
// out 变量无需提前初始化
bool succ = int.TryParse(input, out num);
if(succ) Console.WriteLine(num);
else Console.WriteLine("数字格式错误");
拓展
所有基础数值类型均提供TryParse:double、float、bool 等。
六、out 参数补充(反射 / 转换高频)
规则
-
方法内必须对 out 参数赋值,编译强制校验;
-
调用方变量无需初始化;
-
多用于函数多输出(转换结果、错误码)。
七、拓展
-
属性与普通 Get/Set 函数区别、游戏 UI 刷新实战用法;
-
索引器适用场景、支持多类型重载;
-
反射核心使用流程、性能缺陷与项目限制;
-
is判断、as安全转换与直接强转三者差异; -
TryParse相比 Parse 的优势; -
自动属性与完整带字段属性区分;
-
反射游戏热更新、插件系统底层原理。