Hello World
Spiga

趣味编程:将事件视为对象

2009-09-09 13:11 by 老赵, 12848 visits

如果一个语言(平台)把事件视为对象,则表明它把“事件”作为了语言的一等公民来对待。这意味着,我们可以把一个单独的事件作为参数传递给方法,也可以将其作为一个对象的一部分,这有效地提高语言的抽象能力。试想,如果没有“委托”,在.NET中就无法把“方法”看作是对象,也就很难使用如今各种灵活的抽象方式。同样,由于.NET本身无法将事件作为单个对象处理,因此在某些时候就会束手束脚,也难以引入一些特别的编程模型。

这就是“把事件作为对象进行传递”的实际意义。

上一篇文章里,我们提出了一种“解决方案”,它允许我们编写这样的代码:

class Program
{
    public event EventHandler Submit;
 
    static void Main(string[] args)
    {
        Program p = new Program();
        var ev = EventFactory.Create(() => p.Submit);
        ev += (sender, eventArgs) => Console.WriteLine(sender);
 
        p.Submit("Hello World", EventArgs.Empty);
 
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }
}

看上去挺那么像回事儿的,使用方式和传统的事件似乎没有太大区别。但是文末我提到这里其实有些“糊弄”的意味,而我们的装配脑袋同学、以及“脑袋装配得不输给装配脑袋”的RednaxelaFX,这两位纯爷们也都指出了问题。

信脑袋,得永生。信RednaxelaFX,原地满状态复活。

以上代码的“忽悠”,在于操作Program.Submit的代码处于Program类之内。如果我们想要使用相同的做法操作其他类的事件就做不到了,例如:

public class MyClass
{
    public event EventHandler MyEvent;
}

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        var ev = EventFactory.Create(() => myClass.MyEvent);
    }
}

这样的代码看似没有问题,但是编译器会提示这样的错误:

The event 'SimpleConsole.MyClass.MyEvent' can only appear on the left hand side of += or -= (except when used from within the type 'SimpleConsole.MyClass')

编译器告诉我们,除了在MyClass类的内部,MyEvent事件只能出现在+=或-=操作的左边。之前提到的两位纯爷们在前文的评论中也有过相关及衍生的讨论。因此,我们目前的做法是失败的。

前文的评论中还有朋友提到,我们事实上也可以把一个事件作为参数传递给一个方法(然后在方法里添加或删除处理程序),只要使用ref关键字就可以了,例如:

static void RegisterHandlers(ref EventHandler e) { ... }

然后:

static void Main(string[] args)
{
    var myClass = new MyClass();
    RegisterHandlers(ref myClass.MyEvent);
}

不过很显然,这样的做法也会遇到相同的问题:除非是Program内部的事件,我们不能把它像一个委托对象那样传递。而且,即使可以传递,我们也只能为它添加或删除处理函数,而不能把它作为另一个对象的一部分,然后经过各种处理之后,还可以对这个事件进行操作。

因此,我们要实现的其实是这样一个类型:

public class DelegateEvent<TDelegate>
{
    ...

    public DelegateEvent<TDelegate> AddHandler(TDelegate handler) { ... }

    public DelegateEvent<TDelegate> RemoveHandler(TDelegate handler) { ... }
}

这就是今天“趣味编程”的题目:将DelegateEvent<>类型实现完整,并尽可能做到严谨易用(即适用于各种场合,各种方式进行“构造”)。

所谓“趣味编程”,是指那些我觉得难度适中的小题目,并可以锻炼“编程能力”或“语言类库的掌握程度”。一般来说它们都源自实际项目,只不过改造成“题目”时进行了“抽象”和“提炼”。个人认为它们还是挺适合作为平时的编程练习来使用的,感兴趣的朋友们不妨一试。

至于题目是否真的有“趣味”……这个见仁见智吧。我想,要让那些对于那些视编程如磨难的朋友们感到有趣,应该不比登天要容易一些。(答案

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

70 条回复

  1. good man
    *.*.*.*
    链接

    good man 2009-09-09 13:16:00

    学习了,沙发

  2. 麒麟.NET
    *.*.*.*
    链接

    麒麟.NET 2009-09-09 13:16:00

    trytry

  3. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-09-09 13:17:00

     信脑袋,得用生

    Spelling - -

  4. 老赵
    admin
    链接

    老赵 2009-09-09 13:24:00

    @韦恩卑鄙
    信老赵,读本科。
    信韦恩,不挂科。

  5. 装配脑袋
    *.*.*.*
    链接

    装配脑袋 2009-09-09 13:53:00

    我有点眩晕……

  6. 老赵
    admin
    链接

    老赵 2009-09-09 14:00:00

    @装配脑袋
    看把你乐得……

  7. Jeff Wong
    *.*.*.*
    链接

    Jeff Wong 2009-09-09 14:02:00

    Jeffrey Zhao:
    @韦恩卑鄙
    信老赵,读本科。


    信老赵,不浮躁。

  8. fftt[未注册用户]
    *.*.*.*
    链接

    fftt[未注册用户] 2009-09-09 14:06:00

    我觉得EventAggregator这样的方法就可以了嘛,不用非要一个Event。 具体可参考Prism里面的EventAggregator

  9. 老赵
    admin
    链接

    老赵 2009-09-09 14:09:00

    @fftt
    EventAggregator写一个也是很容易的,只是太重了,适用于“架构”层面,推荐《event-based programming》这本书,在这相关方面谈论很多。
    不过那种“事件”是架构上的概念,而我这里的“事件”单指.NET平台的“元素”,现在是对它的一个轻量封装,而且搞完这个还可以有其他东西基于它好搞,嘿嘿。

  10. Jeff Wong
    *.*.*.*
    链接

    Jeff Wong 2009-09-09 14:20:00

    Jeffrey Zhao:
    @fftt
    EventAggregator写一个也是很容易的,只是太重了,适用于“架构”层面,推荐《event-based programming》这本书,在这相关方面谈论很多。
    不过那种“事件”是架构上的概念,而我这里的“事件”单指.NET平台的“元素”,现在是对它的一个轻量封装,而且搞完这个还可以有其他东西基于它好搞,嘿嘿。


    老赵的评论修改三遍了,是不是?

  11. 老赵
    admin
    链接

    老赵 2009-09-09 14:22:00

    @Jeff Wong
    何止三遍。

  12. Jeff Wong
    *.*.*.*
    链接

    Jeff Wong 2009-09-09 14:26:00

    看来你是要让你每句发言都要禁得起推敲啊
    很好,很负责

  13. Jeff Wong
    *.*.*.*
    链接

    Jeff Wong 2009-09-09 14:36:00

  14. YJJ
    *.*.*.*
    链接

    YJJ 2009-09-09 14:46:00

    信老赵,原地复活满状态无敌三秒:)

  15. 啊嗯哦啊[未注册用户]
    *.*.*.*
    链接

    啊嗯哦啊[未注册用户] 2009-09-09 14:55:00

    赵先生..本科很重要吗?

  16. 老赵
    admin
    链接

    老赵 2009-09-09 15:02:00

    @啊嗯哦啊
    如果你水平很高,那就不重要,否则就很重要。

  17. 老赵
    admin
    链接

    老赵 2009-09-09 15:03:00

    @Jeff Wong
    没啥看法……就是事件啊……

  18. feilng
    *.*.*.*
    链接

    feilng 2009-09-09 15:31:00

    public class DelegateEvent<TDelegate> where TDelegate : class
    {
    private TDelegate mEvent;

    public TDelegate Event
    {
    get { return mEvent; }
    }

    public DelegateEvent<TDelegate> AddHandler(TDelegate handler)
    {
    mEvent = Delegate.Combine(mEvent as Delegate, handler as Delegate) as TDelegate;
    return this;
    }
    ........

  19. 老赵
    admin
    链接

    老赵 2009-09-09 15:35:00

    @feilng
    没有那么简单的……

  20. feilng
    *.*.*.*
    链接

    feilng 2009-09-09 15:37:00

    非严谨猜测版,老实点就自己内部维护列表,并处理线程安全

  21. 杨同学
    *.*.*.*
    链接

    杨同学 2009-09-09 15:43:00

    嗯, 之前有想过这方面的问题。
    老赵, Java没有delegate 而.net 有, 这算不算是C# 在这方面的优势呢? Java 的大部分web框架都采用了MVC模式,而Asp.net 采用的事件驱动。相比之下jsp 网站的应用程序流程更为清晰,有条理。 这点你怎么看?

  22. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-09-09 15:51:00

    Jeffrey Zhao:
    @韦恩卑鄙
    信老赵,读本科。
    信韦恩,不挂科。


    本人挂了28科念了大五 Come on and 信 Me。。。

  23. 老赵
    admin
    链接

    老赵 2009-09-09 15:52:00

    @杨同学
    首先,C#从语言上来说已经远远领先于Java语言。
    其次,事件驱动也可以写的清晰,完全看怎么实现的。
    最后,ASP.NET现在也有ASP.NET MVC框架。

  24. pk的眼泪
    *.*.*.*
    链接

    pk的眼泪 2009-09-09 16:26:00

    @Jeffrey Zhao
    将事件作为委托传递给事件执行方法(参数还是委托类型),不知道这算不算是事件的参数传递

        class Program
        {
            public static event Func<string> MyEvent;
    
            static void Main(string[] args)
            {
                MyEvent += new Func<string>(GetNowDate);            
    
                InvokeEvent(MyEvent);
    
                Console.ReadLine();
            }
    
            public static string GetNowDate()
            {
                return DateTime.Now.ToString();
            }
    
            public static void InvokeEvent(Func<string> e)
            {
                Console.WriteLine(e());
            }
        }
    

  25. 老赵
    admin
    链接

    老赵 2009-09-09 16:29:00

    @pk的眼泪
    我觉得我已经说的很清楚了阿,上一篇文章里连例子都给过了……
    // 你这个事件类型很特别。

  26. 木野狐(Neil Chen)
    *.*.*.*
    链接

    木野狐(Neil Chen) 2009-09-09 16:30:00

    非泛型版本的比较好实现。。。泛型的还没想清楚。

  27. pk的眼泪
    *.*.*.*
    链接

    pk的眼泪 2009-09-09 16:32:00

    @Jeffrey Zhao
    用Func<string>主要是偷懒,省定义一个委托...

  28. 老赵
    admin
    链接

    老赵 2009-09-09 16:33:00

    @木野狐(Neil Chen)
    没看出啥区别阿,赫赫。

  29. 老赵
    admin
    链接

    老赵 2009-09-09 16:33:00

    @pk的眼泪
    我觉得你需要补充一些.NET基础,还有文章需要再理解一下……

  30. 火星人.NET
    *.*.*.*
    链接

    火星人.NET 2009-09-09 16:35:00

    我要是北大青鸟的鸟头人物,我就花重金,让老赵做北大青鸟的形象代言。
    要是不同意,我就雇佣黑社会,天天骚扰他,直到同意为止!

  31. 木野狐(Neil Chen)
    *.*.*.*
    链接

    木野狐(Neil Chen) 2009-09-09 16:38:00

    我是这样做的,比较简单,不完备,不知道是不是基本符合要求:

    var target = new Target();
    var evt = new DelegateEvent<EventHandler>(target, typeof(Target).GetEvent("Observed"));

    EventHandler handler1 = (sender, e) => { Console.WriteLine("handler 1"); };

    evt.AddHandler(handler1)
    .AddHandler((sender, e) => { Console.WriteLine("handler 2"); })
    .RemoveHandler(handler1)
    .RemoveHandler(handler1) // 重复删除也可以的,但判断如果没有这个 handler,只是简单的忽略。
    .AddHandler(handler1)
    .AddHandler(handler1); // 可以重复注册

    target.OnObserved();

    我说的非泛型是因为构造器写的比较丑陋,直接用了反射了。
    本来我试了一下你上一篇文章里的 Expression<Func<TDelegate>> 作为构造器的参数,但貌似 Event 是不能传进去的,如果在不同的类中。所以我就改成这样了。

  32. 木野狐(Neil Chen)
    *.*.*.*
    链接

    木野狐(Neil Chen) 2009-09-09 16:40:00

    另外这个让我有点联想到你前面有几篇文章写到的消息机制了,Ping-Pong 那个什么的。

    特别是事件也可以有返回值,刚才也试了一下,以前没这么用过,感觉跟这个 Ping-Pong 的消息架构能联系起来。

  33. 木野狐(Neil Chen)
    *.*.*.*
    链接

    木野狐(Neil Chen) 2009-09-09 16:41:00

    除了构造函数之外, AddHandler 和 RemoveHandler 还是泛型的。

  34. 老赵
    admin
    链接

    老赵 2009-09-09 16:49:00

    @木野狐(Neil Chen)
    事件的返回值是没有意义的,因为事件其实是一种消息机制,是把消息发出去,不关心结果如何。所以.NET中例如EventHandler等等都是void的。
    一个事件还会有多个handler,你想,如果执行一个Func<int>之后,得到n个Handler返回的int值,你怎么处理呢?

  35. 木野狐(Neil Chen)
    *.*.*.*
    链接

    木野狐(Neil Chen) 2009-09-09 17:07:00

    @Jeffrey Zhao
    我想过这个问题,一般情况下自然是遵循基本的 EventHandler<TArgs> 类型比较规范。利用返回值也许只对一个 handler 的情况有意义,不过也可以在 TArgs 的属性里设置的,所以我也觉得没多大意义,可能只是某种特殊场合的用法。


  36. 老赵
    admin
    链接

    老赵 2009-09-09 17:14:00

    @木野狐(Neil Chen)
    所以我觉得,从概念上说,事件是不应该有返回值得——这里的事件是指广义的事件。
    只不过在.NET的实现方式中,事件是利用了委托,而委托是可以有返回值的。
    如果使用其他方式来实现的事件这个“概念”,如上面提过的EventAggregator,是不会让事件带有返回值的。

  37. 木野狐(Neil Chen)
    *.*.*.*
    链接

    木野狐(Neil Chen) 2009-09-09 17:34:00

    @Jeffrey Zhao
    刚了解了一下 EventAggregator:
    http://martinfowler.com/eaaDev/EventAggregator.html

    这应该就是一个 facade 一类的东西,简化 client 端处理多个事件的麻烦。
    但我感觉这个只是一个架构上的概念,实际没有涉及到 event 的实现。

    不过按这个模式的话,返回值应该是没有的。就算有也被丢弃了。

  38. Jeff Wong
    *.*.*.*
    链接

    Jeff Wong 2009-09-09 18:24:00

    Jeffrey Zhao:
    @Jeff Wong
    没啥看法……就是事件啊……


    李战老师的书《悟透JavaScript》里说,除了基本数据类型如字符串,数值型,布尔型等,Function,Object皆是对象.从本质上说,事件是一个Function还是Object?"将事件视为对象",我感觉脚本已经这么对待了。

  39. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-09 18:38:00

    其实我一直觉得把事件当对象的思路有点偏。

    其实简单的说要实现的功能无非是在任何地方可以监听事件,而不需要知道事件源是什么。那么只要弄一个方法监听事件,发生的时候调用一个委托不就行了?

    简单的说设计一个对象:
    public class EventHandler<T>
    {
    public void Listener( T arg )
    {
    if ( Event != null )
    Event( arg );
    }

    public event Action<T> Event;
    }

    照样编写1-N个T的版本。。。。


    创建对象并挂接到事件:

    var eventObject = new EventHandler<object, EventArgs>();
    obj.SomeEvent += eventObject.Listener;

    然后就可以在任何地方监听这个对象的Event事件。。。。。

  40. 老赵
    admin
    链接

    老赵 2009-09-09 18:41:00

    @Ivony...
    实在没看懂你代码的意思是什么。
    不过没有错的,现在的目标的确就是“在任何地方可以添加/删除事件处理程序,而不需要知道事件源是什么”。
    所以其实,DelegateEvent类是非常简单的东西。

  41. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-09 18:46:00

    Jeffrey Zhao:
    @Ivony...
    实在没看懂你代码的意思是什么。
    不过没有错的,现在的目标的确就是“在任何地方可以添加/删除事件处理程序,而不需要知道事件源是什么”。
    所以其实,DelegateEvent类是非常简单的东西。



    简单的说,就是有一个类型,公开了一个Listener方法,然后你把这个方法挂接到想要被传递的事件上。
    那么当事件被触发的时候,自然就会调用这个Listener方法,而这个方法的实现很简单,就是引发这个类型的Event事件。

    简单的说:
    var eventObject = new EventHandler<object, EventArgs>();
    obj.SomeEvent += eventObject.Listener;


    然后,你就可以把eventObject对象传来传去了。
    如果需要把某个方法挂接到事件上,就直接挂在eventObject.Event事件上就可以了。

  42. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-09 18:48:00

    当然这样的实现方式代码不够好看,并且因为与原事件脱钩,所以如果你在原事件上注册了方法,在这边是无法解除注册的。也无法传递原事件的add和remove。

  43. 老赵
    admin
    链接

    老赵 2009-09-09 19:12:00

    @Ivony...
    这是一个可行的做法,但是相当于又委托了一遍,我觉得不如搞一个对象,提供Add和Remove两个事件就可以了。
    还有一点就是,我这个做法其实是和F#学的,明天就打算引入F#的事件处理模型来“趣味编程”一下玩玩,呵呵。
    了解了F#之后,我意识到把事件作为对象看待,是一件有用且有趣的事情,再加上Reactive Programming……

  44. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-09-09 19:19:00

    韦恩卑鄙:

    Jeffrey Zhao:
    @韦恩卑鄙
    信老赵,读本科。
    信韦恩,不挂科。


    本人挂了28科念了大五 Come on and 信 Me。。。


    高人

  45. iceboundrock
    *.*.*.*
    链接

    iceboundrock 2009-09-09 19:26:00

    可惜C#不能重载()运算符,不然看起来会更接近标准事件。

  46. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-09 19:34:00

    Jeffrey Zhao:
    @Ivony...
    这是一个可行的做法,但是相当于又委托了一遍,我觉得不如搞一个对象,提供Add和Remove两个事件就可以了。
    还有一点就是,我这个做法其实是和F#学的,明天就打算引入F#的事件处理模型来“趣味编程”一下玩玩,呵呵。
    了解了F#之后,我意识到把事件作为对象看待,是一件有用且有趣的事情,再加上Reactive Programming……



    其实我在测试最完美的解决方案,用Emit自己创建一个委托类型然后修改它的逻辑。不过最近真的是忙的焦头烂额。。。。

  47. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-09-09 19:36:00

        public class MyClass
        {
            public event EventHandler MyEvent;
        }
        public class DelegateEvent<TDelegate>
        {
            public Action<TDelegate> _add;
            public Action<TDelegate> _remove;
            
            public DelegateEvent(Action<TDelegate> add, Action<TDelegate> remove){
            }
            public DelegateEvent<TDelegate> AddHandler(TDelegate handler) 
            {
                _add(handler);
                return this;
            }
    
            public DelegateEvent<TDelegate> RemoveHandler(TDelegate handler) 
            {
                _remove(handler);
                return this;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var myClass = new MyClass();
                var ev = new DelegateEvent<EventHandler>((handler) => myClass.MyEvent += handler, (handler) => myClass.MyEvent -= handler);
            }
        }
    

    是这意思?

  48. 老赵
    admin
    链接

    老赵 2009-09-09 20:34:00

    @winter-cn
    没错没错,这是我认为最简单的做法。

  49. 木野狐(Neil Chen)
    *.*.*.*
    链接

    木野狐(Neil Chen) 2009-09-09 21:02:00

    这代码写得好啊... 我就没想到这个,
    var ev = new DelegateEvent<EventHandler>((handler) => myClass.MyEvent += handler, (handler) => myClass.MyEvent -= handler);

    但是作为一个类的构造器而言,感觉这种 api 太难用了点,不便于推广。

  50. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-09-09 22:16:00

    木野狐(Neil Chen):
    这代码写得好啊... 我就没想到这个,
    var ev = new DelegateEvent<EventHandler>((handler) => myClass.MyEvent += handler, (handler) => myClass.MyEvent -= handler);

    但是作为一个类的构造器而言,感觉这种 api 太难用了点,不便于推广。


    我觉得这个可以算是模板方法的动态版本 Event算一个特例吧

  51. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-09-09 22:36:00

    Jeffrey Zhao:
    @winter-cn
    没错没错,这是我认为最简单的做法。


    老赵 下次出的小题要有点彩头才好玩 哈哈哈

  52. 老赵
    admin
    链接

    老赵 2009-09-09 22:58:00

    winter-cn:
    但是作为一个类的构造器而言,感觉这种 api 太难用了点,不便于推广。
    我觉得这个可以算是模板方法的动态版本 Event算一个特例吧


    是啊,有了函数式编程特性后就很少用到模板方法了,因为高阶函数的存在,可以把方法直接作为参数传进去,非常优雅。

  53. 老赵
    admin
    链接

    老赵 2009-09-09 22:58:00

    winter-cn:
    老赵 下次出的小题要有点彩头才好玩 哈哈哈


    但是……这也不是唯一的,也不是所有情况下都是最好的做法亚,呵呵。

  54. 木野狐(Neil Chen)
    *.*.*.*
    链接

    木野狐(Neil Chen) 2009-09-09 23:08:00

    老赵还是发挥开去讲一下传递事件对象的典型应用场景吧。。。呵呵.

  55. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-09-09 23:10:00

    Jeffrey Zhao:

    winter-cn:
    但是作为一个类的构造器而言,感觉这种 api 太难用了点,不便于推广。
    我觉得这个可以算是模板方法的动态版本 Event算一个特例吧


    是啊,有了函数式编程特性后就很少用到模板方法了,因为高阶函数的存在,可以把方法直接作为参数传进去,非常优雅。


    设计模式都是纯OO的设计解决方案
    有了事件之后观察者也不怎么有用了呢

    还有抽象工厂和创建者
    策略模式和状态模式也可以替代了
    貌似命令模式更没用了......

    动态和函数式特性真的能省不少模式呢
    老赵有空不妨写个系列 讨论下有了高阶函数能优雅地替掉多少设计模式

  56. 老赵
    admin
    链接

    老赵 2009-09-09 23:20:00

    木野狐(Neil Chen):老赵还是发挥开去讲一下传递事件对象的典型应用场景吧。。。呵呵.


    其实这就是我搞这些的目的,呵呵。

  57. 老赵
    admin
    链接

    老赵 2009-09-09 23:20:00

    @winter-cn
    说不定是可以这样的……

  58. RednaxelaFX
    *.*.*.*
    链接

    RednaxelaFX 2009-09-10 00:21:00

    韦恩卑鄙:

    Jeffrey Zhao:
    @韦恩卑鄙
    信老赵,读本科。
    信韦恩,不挂科。


    本人挂了28科念了大五 Come on and 信 Me。。。


    本人也挂了十几科读了大五……应该早点信你们的 T T

  59. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-09-10 02:04:00

    RednaxelaFX:
    本人也挂了十几科读了大五……应该早点信你们的 T T


    膜拜ing

  60. Pandora
    *.*.*.*
    链接

    Pandora 2009-09-10 08:57:00

    RednaxelaFX:

    韦恩卑鄙:

    Jeffrey Zhao:
    @韦恩卑鄙
    信老赵,读本科。
    信韦恩,不挂科。


    本人挂了28科念了大五 Come on and 信 Me。。。


    本人也挂了十几科读了大五……应该早点信你们的 T T


    大五同学会。算我一个。。

  61. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-10 09:23:00

    你们慢聊,我抽空研究下IL先。。。

    另,个人觉得模版方法模式是不能被替代的,除非是为了复用而做的继承。其次设计模式本来就很废,讨论能换掉多少没啥意义,再过几年没谁记得这些东西了。

    现在的淫啊,都是该记住的忘了,不该记的天天追着跑。。。。

  62. 老赵
    admin
    链接

    老赵 2009-09-10 09:38:00

    @Ivony...
    没有人说过要完整替代吧,一直说是某些时候可以如何如何的……

  63. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-10 11:16:00

    Jeffrey Zhao:
    @Ivony...
    没有人说过要完整替代吧,一直说是某些时候可以如何如何的……



    那些某些时候应该就是为了复用而继承的情况吧。

  64. 小川
    *.*.*.*
    链接

    小川 2009-09-11 00:44:00

    不如看看actionscript3的事件机制是多么清晰优雅把。

  65. 老赵
    admin
    链接

    老赵 2009-09-11 00:54:00

    @小川
    介绍一下?

  66. 小川
    *.*.*.*
    链接

    小川 2009-09-11 01:04:00

    这个。。。百度一下更快,不过可以看看我用python 访as3的一个事件机制。

    http://www.cnblogs.com/babyfaction/archive/2009/04/12/1433997.html

    那几个代表事件的字符串,可以在事件类里用常量表示。

  67. 老赵
    admin
    链接

    老赵 2009-09-11 08:59:00

    @小川
    这个比较普通吧,随便找个JavaScript都是这样。而且,C#是强类型语言,也不推荐使用magic string。
    而且如果要体现你这段代码的“优势”,还是必须借助动态语言。
    此外,我认为现在封装好了之后,就比你给的代码要好了。你这里的事件依旧不是一等公民。

  68. 小川
    *.*.*.*
    链接

    小川 2009-09-11 23:52:00

    @Jeffrey Zhao
    没有啊,AS3是完全oop的静态语言,强类型的。

  69. 老赵
    admin
    链接

    老赵 2009-09-12 01:38:00

    @小川
    那么你希望我从这段Python代码里看出什么内容阿,呵呵。

  70. 鹤冲天
    *.*.*.*
    链接

    鹤冲天 2009-09-21 17:28:00

    @Jeffrey Zhao
    还真实现了,太不容易了!
    除了AddHandler、RemoveHandler外,还有“判断事件是否为空”比较实用。
    我尝试了N多次实现这个方法都失败了。因为有event关键字,只能+=、-=了,估计实现不了了!

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我