使用扩展方法和接口给对象添加“重置状态”功能
2008-02-12 00:29 无常 阅读(2682) 评论(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; |
} |
} |
使用的时候就很方便了:
不过,这样还是要求必需在默认构造方法中做初始化:(,似乎某本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); |
} |
} |