BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

使用扩展方法对调用进行验证

| 作者 张逸 关注 9 他的粉丝 发布于 2009年3月28日. 估计阅读时间: 6 分钟 | GMTC大前端的下一站,PWA、Web框架、Node等最新最热的大前端话题邀你一起共同探讨。

利用C# 3.0提供的扩展方法技术,可以为已经编译好的程序集类型增加新的方法,从而应对新的扩展。除了在可扩展性方面所具有的优势之外,如果能够合理地结合泛型与类型推断,扩展方法还可以有效降低代码的重复,提高程序的可重用性。例如,这样的方法实现:
public class CustomerDAL
{
    public IEnumerable FindCustomers(string roleName)
    {
        return from customer
             in context.Customer
             where customer.RoleName.Equals(roleName)
             select customer;
    }
}

调用如下方法,可能抛出NullReferenceException异常:
Customer customer = new CustomerDAL().FindCustomers(Role.Admin).First();

我们需要对返回结果进行验证,如果返回为null,则可以抛出自定义异常,或者创建一个空对象,例如:
public IEnumerable FindCustomers(string roleName)
{
    IEnumerable customers = from customer
             in context.Customer
             where customer.RoleName.Equals(roleName)
             select customer;
    if (customers == null)
    {
        throw new MyException("Cann't find the customers.");
    }
    return customers;
}

如果系统有许多方法都需要对返回结果进行验证,则这样的验证逻辑就会充斥在各个方法体中,既不利于重用,也会对未来的修改造成极大的阻碍。当然,我们可以引入Null Object模式来替代对null值的判断逻辑,但这种方式仍然需要为多种类型定义不同的Null Object类型。

Craig Andera在其博客文章中提出使用扩展方法对调用进行验证。他写道:

NullReferenceException异常会抛出,但是我们希望有更具体的异常信息。因此,我们编写了如下的扩展方法:

public static T OrThrow(this T obj, Exception e) {
    if (obj == null) {
        throw e; 
    } 
    return obj;
}

利用OrThrow扩展方法,则之前的调用方式可以修改为:

Customer customer = new CustomerDAL().FindCustomers(Role.Admin).OrThrow(new MyException("Can't find Customer")).First();

Craig Andera提出: 

OrThrow扩展方法对于你所要调用的类型而言是通用的,并且它返回了该类型,所以你可以将其插入到表达式链中,而不会丢失智能感应功能。并且因为类型推断功能,实际上并不需要指定具体的类型。

也就是说,OrThrow扩展方法可以应用到任何类型上,因此它可以在各种类型上重用非空验证甚至是调用验证。借鉴这一思想,我们还可以利用此方法默认实现对象实例的创建,以避免抛出NullReferenceException异常,例如:
public static T Instance(this T obj) where T:new()
{
    if (obj == null)
    {
        obj = new T();
    }

    return obj;
}

由于Instance扩展方法中的类型参数T需要创建实例,因此必须添加new()约束。所以该扩展方法存在一定的局限,例如无法应用在之前的IEnumerable类型上。但对于如下的方法却非常有效:
public class ListObject
{
    public List Foo()
    {
        return null;
    }
}

通过Instance扩展方法,可以安全地调用List的相关属性和方法,例如Count属性:
Console.WriteLine(new ListObject().Foo().Instance().Count);

控制台打印出来的结果为0。如果没有Instance扩展方法,则会抛出NullReferenceException异常。

作为C# 3.0增加的新特性,扩展方法在大量项目中得到了广泛地应用,但绝不仅仅是提高可扩展性这么简单。在进行项目开发时,若能适当地考虑使用扩展方法,说不定会带来出奇制胜的效果。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

关于文章代码的几个问题 by 白 李

第一 我在我的公共函数里面写了
public static T Instance(this T obj) where T:new()
{
if (obj == null)
{
obj = new T();
}

return obj;
}

但是报错
Constraints are not allowed on non-generic declarations

Instance函数 by qb li

public static T Instance<T>(this T obj) where T:new()
{
if (obj == null)
{
obj = new T();
}

return obj;
}

我的必须这样才通得过,大家有按文章上的那种通过的吗?该怎么写?</t>

Re: 关于文章代码的几个问题 by qb li

博客原文:
public static T OrThrow<T>(this T obj, Exception e) {
if (obj == null) {
throw e;
}
return obj;
}
下面回复
That should be

public static T Instance<T>(this T obj) where T: new()

The <T> indicates that the method is generic on

InfoQ的编辑啊</t></t></t>

Re: 关于文章代码的几个问题 by 白 李

thank you
搞定!谢谢了 !。。。估计编辑是忘记了:)

不是忘记,而是后台发布时,这些泛型参数被过滤了 by 张 逸

文中的代码段依次为:
1.
public class CustomerDAL
{
public IEnumerable<Customer> FindCustomers(string roleName)
{
return from customer
in context.Customer
where customer.RoleName.Equals(roleName)
select customer;
}
}
2.
public IEnumerable<Customer> FindCustomers(string roleName)
{
IEnumerable<Customer> customers = from customer
in context.Customer
where customer.RoleName.Equals(roleName)
select customer;
if (customers == null)
{
throw new MyException("Cann't find the customers.");
}
return customers;
}
3.
public static T OrThrow<T>(this T obj, Exception e) {
if (obj == null) {
throw e;
}
return obj;
}
4.
public static T NullObject<T>(this T obj) where T:new()
{
if (obj == null)
{
obj = new T();
}
return obj;
}
5.
public class ListObject
{
public List<string> Foo()
{
return null;
}
}</string></t></t></customer></customer></customer>

Re: 关于文章代码的几个问题 by 张 逸

在我的博客中,代码是正确的。

www.agiledon.com/post/2009/03/Use-Extension-Met...

c# 扩展方法奇思妙用 by 陆 大鹏

更多扩展方法,请参见我的系列文章:《c# 扩展方法奇思妙用》

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

7 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT