代码改变世界

使用扩展方法和接口给对象添加“重置状态”功能

2008-02-12 00:29  无常  阅读(2680)  评论(7编辑  收藏  举报

项目中有些对象经常需要重置部分或全部属性到初始状态,想给这些类全部都加上个Reset()方法,又显得太冗余。Q.yuhen的这个Post中提出一种思路,使用默认构造函数来重置状态,这样实现:

class MyClass
{
public int X { get; set; }
public string S { get; set; }
public MyClass()
{
    X = 1234;
    S = "abc";
}
public void Reset()
{
    var ctor = this.GetType().GetConstructor(BindingFlags.Instance | BindingFlags.Public,
     null, new Type[0], null);
    ctor.Invoke(this, null);
}
}

这个方法很巧妙,但还是需要在每个类中都重复添加这个Reset()方法,而且每个都是相同的代码,不是很满意。

 

于是,探讨一种新的方法-----使用c#3.0的扩展方法(Extension Methods)功能来实现此功能,实现起来就二行代码。

    public static class ResetableImpl
    {
        public static void Reset(this object obj)
        {
            var ctor = obj.GetType().GetConstructor(BindingFlags.Instance | BindingFlags.Public,
                null, new Type[0], null);
            ctor.Invoke(obj, null);
        }
    }

这样就给所有的类都扩展了Reset()方法。

可是,这样实现有个很大的局限性,就是这个类必需有个默认构造方法,而且是在默认构造方法中初始化才行,否则是在做无用功。另外,我们并不是所有的类都需要Reset()方法,这样直接给祖先object扩展也不太合情理。

再改进,配合接口使用。

先定义一个接口IResetable:

public interface IResetable{}

这个接口什么也不做,只是个幌子,然后我们给这个接口添加扩展方法。(有关给接口添加扩展方法的事项,可以看下蝈蝈俊.net的这个POST:C#3.0 中使用扩展方法来扩展接口。)

现在来修改一下这个添加扩展方法的这个class,将扩展对象由object改为IResetable接口:

    public static class ResetableImpl
    {
        public static void Reset(this IResetable obj)
        {
            var ctor = obj.GetType().GetConstructor(BindingFlags.Instance | BindingFlags.Public,
                null, new Type[0], null);
            ctor.Invoke(obj, null);
        }
    }

然后,给我们的类加上IResetable接口,如这样:

    class Mail : IResetable
    {
        public string Subject { get; set; }
        public string Body { get; set; }
        public MailAddress From { get; set; }
        public MailAddress To { get; set; }
        public Mail()
        {
            this.Subject = "";
            this.Body = "";
            this.From = null;
            this.To = null;
        }
    }

使用的时候就很方便了:

    public class Program
    {
        static void Main(string[] args)
        {
            Mail mail = new Mail();
 
            mail.From = new MailAddress("wuchangx@qq.com");
            mail.To = new MailAddress("friend@anywhere.com");
            mail.Subject = "hi friend";
            mail.Body = "mail content.";
 
            Console.WriteLine("1\nFrom={0},To={1},\nSubject={2},Body={3}",
             mail.From, mail.To, mail.Subject, mail.Body);
 
            mail.Reset();
 
            Console.WriteLine("2\nFrom={0},To={1},\nSubject={2},Body={3}",
             mail.From, mail.To, mail.Subject, mail.Body);
 
            Console.ReadKey();
        }
    }
 

测试代码

image

运行结果

不过,这样还是要求必需在默认构造方法中做初始化:(,似乎某本c#大作中也提倡在构造方法中初始化的。

如果担心每次调用时Reflection的性能太低,那再改进一下:

    public static class ResetableImpl
    {
        private static object lockObject = new object();
        private static Dictionary<string, ConstructorInfo> dct = new Dictionary<string, ConstructorInfo>();
        public static void Reset(this IResetable obj)
        {
            string key = obj.GetType().ToString();
            ConstructorInfo ctor =null ;
            if ( dct.ContainsKey(key) ==false)
            {
                lock (lockObject)
                {
                    if (dct.ContainsKey(key) == false)
                    {
                        ctor = obj.GetType().GetConstructor(BindingFlags.Instance | BindingFlags.Public,
                            null, new Type[0], null);
                    }
                }
            }
            else
            {
                ctor = dct[key];
            }
            ctor.Invoke(obj, null);
        }
    }

出处:http://wuchang.cnblogs.com