10.属性
属性本质上是方法,每个属性都只是类型中定义的一对方法和一些元数据。get和set访问器都是以内联的方式直接将代码编译到调用它的方法中,性能没损失。
10.1无参属性
类型的字段尽量别公开,否则很容易因为不恰当地使用字段而破坏对象的状态,例如以下代码破坏了一个Employee对象:e.Age = -5;
,所以,强烈建议将所有字段都设为private,利用属性来公开私有字段。属性举例:
private int mAge;
public int Age{
get { return mAge; }
set{
if (value < 0)
Console.WriteLine("error");
else
mAge = value;
}
}
注意:属性不能重载,即不能定义名称相同、类型不同的两个属性;属性不能作为out或ref参数传给方法。
自动实现的属性(简称AIP)
只是单纯地为了封装一个私有字段:public String Name { get; set; }
,私有字段自动生成并不显示。
注意:如果想手动初始化AIP,就只有在构造器中显示初始化:public Program(String mName) { Name = mName;}
;AIP需同时有get和set,否则就AIP没意义了;不能显示实现get或set的其中一个,而让另一个自动实现。
对象初始化语法
构造一个对象并设置对象的一些公共属性的简化写法:Employee e = new Employee() { Name = "jump", Age = 23 };
等价于Employee e = new Employee(); e.Name = "jump"; e.Age = 23;
.如果调用的是Employee 的无参构造器,那么还可以去掉花括号前的小括号。
集合初始化语法
如果属性的类型是List这种,也就是,如果属性的类型实现了IEnumerable或IEnumerable接口,属性就是集合类型。ClassRoom cr = new ClassRoom { StudentNames = { "jump", "chen" } };
public sealed class ClassRoom {
private List<string> mStudentName = new List<string>();
public List<string> StudentNames { get { return mStudentName; } } // 不需要实现set
}
匿名类型
一条龙服务:不需要提供类名就可以定义类型,构造实例,初始化属性。var obj = new { Name = "jump", Age = 23 };
,但所有属性都是只读的。
10.2有参属性(又叫索引器)
索引器的get至少接收一个参数(this[]里的参数),set至少接收两个参数(this[]里的参数和value)。定义索引器举例:public int this[string mName]{...}
,调用索引器举例:pp["jump"] = 100;
,其中"jump"和this[]里的参数mName对应,100和value对应。
this指向对象,C#只允许在对象上定义索引器,可以像访问数组一样访问对象(必须传一个参数进来,可理解为重载了“[ ]”操作符)。
字典就实现了一个索引器,根据一个键返回与该键关联的值。
11.事件
没啥好讲的,Show the code
// 第一步:定义附加信息的类型
// 这些附加信息会发给事件接收者,附加信息类应继承自System.EventArgs,类名以EventArgs结尾
// 如果没有需要发送给事件接收者的附加信息,就没必要定义这个类,直接用EventArgs.Empty
internal sealed class NewMsgEventArgs : EventArgs
{
private readonly string mFrom;
private readonly string mTo;
private readonly string mContent;
internal NewMsgEventArgs(string from, string to, string content)
{
mFrom = from;
mTo = to;
mContent = content;
}
public string From { get { return mFrom; } }
public string To { get { return mTo; } }
public string Content { get { return mContent; } }
}
internal class MsgManager // 可被继承的
{
// 第二步:在MsgManager类里定义事件成员(一个委托字段),System.EventHandler是委托类型,它的定义:
// public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
// 限制了接收者类型里的接收方法的形式为:
// void MethodName(Object sender, NewMsgEventArgs e){ } sender:事件源 e:包含事件数据的对象
public event EventHandler<NewMsgEventArgs> NewMsg; // Bell类的构造函数里就引用了这个NewMsg
// 第三步:定义方法来调用接收者类型里的接收方法
protected virtual void OnNewMsgComing(NewMsgEventArgs e) // 可被子类重写
{
// 为了线程安全,将对委托字段的引用复制到临时字段中,Volatile.Read读取委托字段NewMsg的引用
EventHandler<NewMsgEventArgs> temp = Volatile.Read(ref NewMsg); // temp的参数=接收方法的参数
if (null != temp)
temp(this, e); // 调用委托列表里的所有委托的方法,该例是通过调用接收者类型的构造函数来添加委托
}
// 第四步:将附加信息构建成对象e,并将e传给第三步的OnNewMsgComing方法
public void SimulateNewMsg(string from, string to, string content)
{
NewMsgEventArgs e = new NewMsgEventArgs(from, to, content);
OnNewMsgComing(e);
}
// 被模拟触发类Simulate里的Main函数调用
public static void Go()
{
MsgManager mm = new MsgManager();
Bell bell = new Bell(mm);
mm.SimulateNewMsg("jump", "bill", "I can do it!");
bell.Unregister(mm);
mm.SimulateNewMsg("bill", "jump", "Come on!");
//若没有附加信息,就不用调用mm.SimulateNewMail,像下面这样写就好了,也就没必要有SimulateNewMsg函数了
//EventArgs e = EventArgs.Empty;
//mm.OnNewMsgComing(e);
}
}
// 接收者类型Bell
internal sealed class Bell// 当来了一条消息,就要响铃,并显示消息来自谁,送给谁,具体内容
{
public Bell(MsgManager mm)
{
mm.NewMsg += StartBell; // 给事件NewMsg添加委托,也就是在委托列表里添加委托
}
// 第二步的注释里有 接收者类型(Bell)里的接收方法(StartBell)的形式
private void StartBell(Object sender, NewMsgEventArgs e)
{
Console.WriteLine($"----StartBell----from:{e.From},to:{e.To},content:{e.Content}");
}
public void Unregister(MsgManager mm)
{
mm.NewMsg -= StartBell; // 给事件NewMsg删除委托,也就是在委托列表里删除委托
}
}
// 模拟触发类,将附加信息传给第四步的SimulateNewMsg方法
internal class Simulate
{
static void Main()
{
MsgManager.Go();
Console.ReadLine();
}
}
补充点1:上述代码使用了编译器隐式实现的事件:public event EventHandler<NewMsgEventArgs> NewMsg;
,还可以这样显示实现事件(并不是真正的显示,相对显示吧),代码如下:
// 定义一个私有委托字段(引用类型),该字段指向委托列表头部
private EventHandler<NewMsgEventArgs> mNewMsg;
// 手动定义事件成员
public event EventHandler<NewMsgEventArgs> NewMsg
{
add { mNewMsg += value; } // 给委托列表添加委托
remove { mNewMsg -= value; }
}
EventHandler<NewMsgEventArgs> temp = Volatile.Read(ref NewMsg);
也要改为EventHandler<NewMsgEventArgs> temp = Volatile.Read(ref mNewMsg);
补充点2:可以使用扩展方法替换OnNewMsgComing()里的所有代码
// 添加扩展方法
public static class EventArgsExtensions
{
public static void Raise<TEvenArgs>(TEvenArgs e, Object sender,
ref EventHandler<TEvenArgs> delegateFiled)
{
EventHandler<TEvenArgs> temp = Volatile.Read(ref delegateFiled);
if (null != temp) { temp(sender, e); }
}
}
再用e.Raise(this, ref mNewMsg);
替换OnNewMsgComing()里的所有代码