对象事件,它们是如何实现的

时间:2010-04-21 18:11:43

标签: design-patterns events observer-pattern

事件非常棒,我不知道没有它们会怎么做,但对我来说这是个谜。

我在谈论某种意义上的事件,如果属性或价值,特殊事件发生,则会调用函数。

我只有最简单的想法,这些实际上是如何工作的。我知道这是一个观察者模式,但我不知道它是如何工作的和/或如何实现它。

有人可以向我解释一下吗?

4 个答案:

答案 0 :(得分:2)

答案 1 :(得分:2)

据我所知,你问的是事件是如何运作的。你还没有说明你要问的语言/平台,所以我会用我所知道的(.Net)来回答,虽然我确信很多平台都是相似的,但请记住我说的可能不是无处不在。

我将从最低级别开始并向上工作。

功能指针

在大多数语言中都有函数指针的概念。在像C ++这样的语言中,您可以逐字存储指向方法的内存地址的指针。在像Lisp或F#这样的函数式语言中,函数是关键,它是您可以存储和传递函数引用的语言的关键部分。在.net中,函数指针是使用委托实现的。

<强>代表

In .Net事件是使用代理实现的。 delegate是类型安全函数指针。它是指向函数的指针,仅限于特定类型,并在编译时根据该类型进行检查。您可以触发委托,并调用它指向的功能。

<强>组播

multicast delegate是一个形成代表集合的类。它在内部使用一个列表来存储多个委托。当您调用add或执行+=时,您只需将新的委托(函数指针)添加到多播的内部列表中。可以触发多播委托实例,它只是向下移动列表并按顺序在内部触发每个委托。

<强>事件

event只是一个关键字,它会在支持事件的多播委托之上添加一些额外的限制。例如(除其他事项外)在声明多播委托实例时使用event关键字限制它,以便只能从声明它的类中触发它。

所以,总结一下。事件只是一个函数指针列表。当您订阅简单时,将指向函数的指针添加到列表中。当事件被触发时,它只需向下移动列表并触发它知道的每个函数指针。

显然,正如我在开始时所说的那样,每种语言/环境都会有所不同,但如果维护一个简单的函数指针列表的想法可能相当普遍,我也不会感到惊讶。

如果这是您感兴趣的平台,Jon Skeet有一个很好的article on events in .Net,您应该阅读以获取更多信息。

答案 2 :(得分:0)

事件实际上非常简单。

首先,Object定义了其他对象可以订阅的事件。当Object注册一个Event时,Object存储一个在事件发生时将被调用的函数引用(或委托)。

接下来,感兴趣的Object通过将函数引用传递给Observable对象来订阅事件(该函数必须与Observable类提供的签名匹配)。

当事件发生时,Observable类调用适当的方法。

这是一个快速示例(在C#中):

// Specifies the signature for the event and stores the reference
public delegate void ChangedEventHandler(object sender, EventArgs e);

public class ObservableObject
{
    // Creates an event called Changed using the signature provided by the 
    // delegate.
    public event ChangedEventHandler Changed;

    // This is the method we're interested in notifying subscribers about.
    public void SomeInterestnigMethod()
    {
        // Something just changed, notify subscribers
        Changed(this, new EventArgs());
    }
}

然后在另一个想要订阅Changed事件的类中:

public class Observer
{
    ObservableObject _obj = new ObservableObject();

    public Observer()
    {
        // Pass the function reference to objChangedHandler to the
        // Observable Object.
        _obj.Changed += objChangedHandler;
    }

    public void objChangedHandler(object sender, EventArgs e)
    {
        // Handle the event here
    }
}

答案 3 :(得分:0)

你的观察者需要一个接口(在接口关键字意义上不一定是例如java / c#) - 你需要以某种方式知道在需要时调用哪个方法 通知他们。观察者注册他们的兴趣并将它们添加到列表中。

每当你收到要通知的内容时,你都会查看观察者列表 在每个人上调用一个方法。当有人不想再收到通知时,只需将其从观察员名单中删除即可。

这是一个c#示例,不使用c#中的内置'events'或委托:

using System;
using System.Collections.Generic;
namespace ObserverTest
{

interface IInvestor
{
    void Update(Stock stock);
}


abstract class Stock
{
    private string _symbol;
    private double _price;
    private List<IInvestor> _investors = new List<IInvestor>();
    // Constructor
    public Stock(string symbol, double price)
    {
        this._symbol = symbol;
        this._price = price;
    }
    public void Attach(IInvestor investor)
    {
        _investors.Add(investor);
    }
    public void Detach(IInvestor investor)
    {
        _investors.Remove(investor);
    }
    public void Notify()
    {
        foreach (IInvestor investor in _investors)
        {
            investor.Update(this);
        }
        Console.WriteLine("");
    }
    public double Price
    {
        get { return _price; }
        set
        {
            if (_price != value)
            {
                _price = value;
                Notify();
            }
        }
    }
    public string Symbol
    {
        get { return _symbol; }
    }
}
class IBM : Stock
{
    public IBM(string symbol, double price)
    : base(symbol, price)
    {
    }
}



class Investor : IInvestor
{
    private string _name;
    // Constructor
    public Investor(string name)
    {
        this._name = name;
    }
    public void Update(Stock stock)
    {
        Console.WriteLine("Notified {0} of {1}'s " +
        "change to {2:C}", _name, stock.Symbol, stock.Price);
    }
}
class MainApp
{
    static void Main()
    {

        IBM ibm = new IBM("IBM", 120.00);
        ibm.Attach(new Investor("Sorros"));
        ibm.Attach(new Investor("Berkshire"));
        ibm.Price = 120.10;
        ibm.Price = 121.00;
        ibm.Price = 120.50;
        ibm.Price = 120.75;

        Console.ReadKey();
    }
}
}