随笔 - 343  文章 - 8 评论 - 499 trackbacks - 19
<2011年9月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

公告:发表时间超过两个月的随笔的源码一般都被删除了,请勿留言索取。

昵称:天方
园龄:6年2个月
粉丝:27
关注:1

搜索

 
 

常用链接

最新随笔

随笔分类

随笔档案

文章分类

.net学习

最新评论

阅读排行榜

评论排行榜

推荐排行榜

在面向对象语言中,我们往往通过继承来实现代码的复用,下面代码就演示了一个简单的Log功能的例子:

    class Logger<T>

    {

        Subject<T> subject = new Subject<T>();

 

        public void LogMessage(T msg)

        {

            subject.OnNext(msg);

        }

 

        public void Subscribe(Action<T> msgHanlder)

        {

            subject.Subscribe(msgHanlder);

        }

    }

 

    class MyClass1:Logger<string>

    {

 

    }

 

    class MyClass2:Logger<int>

    {

 

    }

然而,Log功能一般只是一种附加功能,在实际应用中,我们的子类往往需要从别的更复杂的类中继承而来,但此时如果要服用Log功能该怎么办呢?对于C++这种支持多重继承的语言来说,直接将Logger作为另一个父类即可附加该功能。然而,对于C#这种只支持单一父类的语言来说,一般只能采用组合的方式来复用Log功能了,基本方式如下:

    class MyClass1 : Base1

    {

        Logger<string> logger = new Logger<string>();

 

        public void LogMessage(string msg)

        {

            logger.LogMessage(msg);

        }

 

        public void Subscribe(Action<string> msgHanlder)

        {

            logger.Subscribe(msgHanlder);

        }

    }

 

    class MyClass2 : Base2

    {

        Logger<string> logger = new Logger<string>();

 

        public void LogMessage(string msg)

        {

            logger.LogMessage(msg);

        }

 

        public void Subscribe(Action<string> msgHanlder)

        {

            logger.Subscribe(msgHanlder);

        }

    }

这种方法带来的一个不好的地方在于:每一个子类都必须封装一遍LogMessage和Subscribe方法。如果子类较多,代码非常难看。好在C# 3.0带来了扩展属性的方法,通过它,则可以很容易类似C++之类的多重继承的功能。

    interface ILogger<T>

    {

 

    }

 

    class MyClass1 : ILogger<string>

    {

    }

 

    class MyClass2 : ILogger<int>

    {

    }

 

    static class LoggerExtend

    {

        static Subject<T> GetLoggerInfo<T>(this ILogger<T> logger)

        {

            throw new NotImplementedException();

        }

 

        public static void LogMessage<T>(this ILogger<T> logger, T msg)

        {

            logger.GetLoggerInfo().OnNext(msg);

        }

 

        public static void Subscribe<T>(this ILogger<T> logger, Action<T> msgHanlder)

        {

            logger.GetLoggerInfo().Subscribe(msgHanlder);

        }

    }

 

这种模式就很完美的解决了这一问题,子类只要实现ILogger这一接口,无需写任何代码,就拥有了Log功能。

但是,上面的方法还不能运行,最关键的GetLoggerInfo方法还没有实现,最直接的实现方法如下:

        static Dictionary<object, object> dic = new Dictionary<object, object>();

 

        static Subject<T> GetLoggerInfo<T>(this ILogger<T> logger)

        {

            object subject = null;

            if (!dic.TryGetValue(logger, out subject))

            {

                subject = new Subject<T>();

                dic.Add(logger, subject);

            }

 

            return subject as Subject<T>;

        }

这样处理后,虽然代码功能看起来正常了,然而却存在一个非常大的隐患——logger无法释放:一旦logger使用后,在静态变量dic中就一直保存着该对象的引用,这样就永远无法释放。为了解决这一问题,我们必须将这个方法改一下,改成使用弱引用来维护logger引用,当logger释放后,其关联的subject对象也跟着一起释放。修改后的代码如下:

    static class LoggerExtend

    {

        class WeakObject : WeakReference

        {

            public object Tag { get; private set; }

 

            public WeakObject(object target,object tag)

                : base(target)

            {

                this.Tag = tag;

            }

        }

 

        static Dictionary<int, WeakObject> dic = new Dictionary<int, WeakObject>();

        static Timer gcTimer = null;

 

        static Subject<T> GetLoggerInfo<T>(this ILogger<T> logger)

        {

            lock (dic)

            {

                var key = logger.GetHashCode();

                WeakObject weakobj = null;

                if (!dic.TryGetValue(key, out weakobj))

                {

                    var subject = new Subject<T>();

                    weakobj = new WeakObject(logger, subject);

                    dic.Add(key, weakobj);

 

                    Console.WriteLine("add "+key);

                    if (gcTimer == null)

                        gcTimer = new Timer(_ => GcWeakObject(), null, 0, 10000);

                }

                return weakobj.Tag as Subject<T>;

            }

           

 

        }

 

        static void GcWeakObject()

        {

            lock (dic)

            {

                var deadObj = dic.Where(i => !i.Value.IsAlive).ToArray();

                foreach (var item in deadObj)

                {

                    if (item.Value.Tag is IDisposable)

                        (item.Value.Tag as IDisposable).Dispose();

                    dic.Remove(item.Key);

                }

 

                if (dic.Count == 0 && gcTimer != null)

                {

                    gcTimer.Dispose();

                    gcTimer = null;

                }

            }

        }

 

        public static void LogMessage<T>(this ILogger<T> logger, T msg)

        {

            logger.GetLoggerInfo().OnNext(msg);

        }

 

        public static void Subscribe<T>(this ILogger<T> logger, Action<T> msgHanlder)

        {

            logger.GetLoggerInfo().Subscribe(msgHanlder);

        }

    }

最后总结一下吧,本文通过C#的扩展方法和弱引用,简单的实现了一个类似C++多重继承的功能,方便那种在大量地方使用的小功能的扩展。当然,这里的解决方案也不完美,一个很明显的问题就是扩展功能对象无法及时回收,需要等到计时器超时,也不支持手动Dispose。这种方式还是比较容易扩展的,我在这里只是抛砖引玉,如果谁有更好的方法,欢迎一起讨论。

 

posted on 2011-09-16 20:42 天方 阅读(132) 评论(0) 编辑 收藏