Event事件
想要了解C#事件,先了解→C#委托
委托通常存在两个角色:广播者和订阅者
订阅者——指的是委托声明
广播者——指的调用委托的方法
可以参考 C#委托(上)的多播,
订阅者可以通过+=和-=来控制监听方法的数量。
这样的操作,被C#简化成了事件,
一方面是为了简化代码,另一方面是为了防止互相干扰。
需要注意的一点:
▢ 事件是一种结构,用于实现广播者/订阅者模型,它只暴露了所需的委托特性的部分子集。
如何声明事件?
▢ 最简单的声明方式就是在委托前面加上event关键字
public delegate void PriceChangedHandler(decimal oldPrice,decimal newPrice);
public class Broadcaster
{
// Event declaration
public event PriceChangedHandler PriceChanged;
}
▢ Broadcaster类型里面的代码拥有对PriceChanged的完全访问权,在这里就可以把它当作委托。
▢ 而Broadcaster类型之外的代码只能对PriceChanged这个event进行+=和-=操作。
以上两点就是描述的“部分子集”的特性。
标准的事件模式
▢ 为编写事件,.NET定义了一个标准的模式
▢ System.EventArgs,一个预定义的框架类,除了静态的Empty属性之外,它没有其它成员。
▢ EventArgs是为事件传递信息的类的基类。
public class PriceChangedEventArgs:System.EventArgs
{
public readonly decimal LastPrice;
public readonly decimal NewPrice;
public PriceChangedEventArgs(decimal lastPrice,decimal newPrice)
{
LastPrice=lastPrice;
NewPrice=newPrice;
}
}
接下来,为事件选择和定义委托
▲返回类型必须是void;
▢ 接收两个参数,第一个参数类型是object,第二个参数类型是EventArgs的子类。第一个参数表示事件的广播者,第二个参数包含需要传递的信息;
▢ 名称必须以EventHandler结尾。
☆ Framework定义了一个满足上述规则的泛型委托System.EventHandler<T>
public delegate void EventHandler<TEventArgs>
(object source,TEventArgs e) where TEventArgs:EventArgs;
然后,针对选择的的委托定义事件
▢ 方法名必须和事件一致,前面再加上On,接收一个EventArgs参数
public class Stock
{
. . .
public event EventHandler<PriceChangedEventArgs> PriceChanged;protected virtual void OnPriceChanged(PriceChangedEventArgs e)
{
if(PriceChanged!=null) PriceChanged (this,e);
}
}
完整的例子
namespace 事件
{
public class PriceChangedEventArgs : EventArgs
{
public readonly decimal LastPrice;
public readonly decimal NewPrice;
public PriceChangedEventArgs(decimal lastPrice,decimal newPrice)
{
LastPrice = lastPrice;
NewPrice = newPrice;
}
}public class Stock
{
string symbol;
decimal price;
public Stock(string symbol)
{
this.symbol = symbol;
}//定义泛型委托
public event EventHandler<PriceChangedEventArgs> PriceChanged;//用于触发事件
protected virtual void OnPriceChanged(PriceChangedEventArgs e)
{
//this代指Stock这个类本身,因为它是广播者
//这里的?是一个简写方式,目的是线程安全
PriceChanged?.Invoke(this, e);
}//定义Price取值和赋值的方法(定义属性)
public decimal Price
{
get { return price; }
set
{
if (price == value) return;
decimal oldPrice = price;
price = value;
OnPriceChanged(new PriceChangedEventArgs(oldPrice, price));
}
}
}
class Program
{
static void Main(string[] args)
{
Stock stock = new Stock("MSFT");
stock.Price = 120;
stock.PriceChanged += stock_PriceChanged;//增加相应的事件,以便于我们观察
stock.Price = 135;//修改Price,导致触发 标准模式的方法,这就说明存在监听
Console.Read();
}
//标准模式
static void stock_PriceChanged(object sender,PriceChangedEventArgs e)
{
if ((e.NewPrice - e.LastPrice)/ e.LastPrice > 0.1M)
{
Console.WriteLine("Alert,10% stock price increase!");
}
}
}
}//到此为止,你应该明白了C# Winform里面的click方法怎么来的了吧
输出:
Alert,10% stock price increase!
▢ 多线程的场景下,你需要在测试或调用前,把委托赋给一个临时变量,来避免线程相关的错误:
var temp=PriceChanged;
if(temp!=null) temp(this,e);
▢ 在C#6.0之后,可以这样写:
PriceChanged?.Invoke(this,e);
非泛型的EventHandler
▢ 当事件不携带多余的信息的时候,可以使用非泛型的EventHandler委托。
▢ EventArgs.Empty属性
举例:
public class Stock
{
string symbol;
decimal price;public Stock(string symbol) { this.symbol = symbol; }
public event EventHandler PriceChanged;
protected virtual void OnPriceChanged(EventArgs e)
{
PriceChanged?.Invoke(this, e);
}public decimal Price
{
get { return price; }
set
{
if (price == value) return;
price = value;
OnPriceChanged(EventArgs.Empty);
}
}
}
事件访问器
▢ 事件访问器是事件的+=和-=函数的实现,
public event EventHandler PriceChanged;
▢ 编译器会把它转化为:
▢ 一个私有的委托字段
▢ 一对公共的事件访问器函数(add_PriceChanged和remove_PriceChanged),这两个函数的实现会把+=和-=操作交给私有的委托字段。
▢ 也可以显式的定义事件访问器
大概是这个样子的:
private EventHandler priceChanged;
public event EventHandler PriceChanged
{
add{priceChanged+=value;}
remove{priceChanged-=value;}
}
什么时候显式定义事件访问器
▢ 当事件访问器仅仅是另一个广播事件的类的中继。
▢ 当类暴露大量event,但是大部分时候都只有少数的订阅者存在。
▢ 显式实现一个声明了事件的接口。
事件修饰符
▢ virtual,可以被重写;abstract,sealed,static。
public class Foo
{
public static event EventHandler<EventArgs> StaticEvent;
public virtual event EventHandler<EventArgs> VirtualEvent;
}
Today's comments have reached the limit. If you want to comment, please wait until tomorrow (UTC-Time).
There is 17h14m28s left until you can comment.