当前位置:  开发笔记 > 编程语言 > 正文

将功能分为验证和实施?为什么?

如何解决《将功能分为验证和实施?为什么?》经验,为你挑选了2个好方法。

我正在阅读一本C#书,其中作者(一些名叫Jon Skeet的家伙)实现了Where类似的功能

public static IEnumerable Where ( this IEnumerable source, Funct predicate ) 
{
    if ( source == null || predicate == null ) 
    {
        throw new ArgumentNullException();
    }
    return WhereImpl(source, predicate);
}

public static IEnumerable WhereImpl ( IEnumerable  source, Func predicate ) 
{
    foreach ( T item in source ) 
    {
      if ( predicate(item) )  
      {
         yield return item;
      }
    }

}

现在,我完全理解这是如何工作的,它相当于

public static IEnumerable Where ( this IEnumerable source, Funct predicate ) 
{
    if ( source == null || predicate == null ) 
    {
        throw new ArgumentNullException();
    }
    foreach ( T item in source ) 
    {
      if ( predicate(item) )  
      {
         yield return item;
      }
    }
}

这就提出了一个问题,即为什么会将这些函数分成2个函数,因为会有内存/时间开销,当然还有更多的代码.我总是验证参数,如果我开始写这个例子,那么我将编写两倍的代码.是否有一些思想认为验证和实施应该是单独的功能?



1> Jon Skeet..:

原因是迭代器块总是很懒惰.除非你调用GetEnumerator()然后MoveNext(),方法中的代码将不会被执行.

换句话说,考虑这种对"等效"方法的调用:

var ignored = OtherEnumerable.Where(null, null);

没有异常被抛出,因为你没有打电话GetEnumerator()然后MoveNext().将其与我的版本进行比较,无论返回值如何使用,都会立即抛出异常...因为它只是热切地验证之后才使用迭代器块调用该方法.

请注意,async/await具有类似的问题 - 如果您有:

public async Task FooAsync(string x)
{
    if (x == null)
    {
        throw new ArgumentNullException(nameof(x));
    }
    // Do some stuff including awaiting
}

如果你打电话给这个,你最终会得到一个错误Task- 而不是NullReferenceException被抛出.如果等待返回Task,抛出异常,但这可能不是您调用方法的地方.在大多数情况下这没关系,但值得了解.



2> Matías Fidem..:

它可能取决于场景和您的编码风格.当您使用yield创建迭代器时,Jon Skeet绝对正确地说明为什么它们应该分开.

顺便说一句,我认为在这里添加我的两分钱可能很有意思:使用代码合同(即按合同设计)的相同代码以不同的方式运行.

前置条件不是迭代器块的一部分,因此,如果不满足整个前置条件,则以下代码将立即抛出合同异常:

public static class Test
{
    public static IEnumerable Where(this IEnumerable source, Func predicate)
    {
        Contract.Requires(source != null);
        Contract.Requires(predicate != null);

        foreach (T item in source)
        {
            if (predicate(item))
            {
                yield return item;
            }
        }
    }
}

// This throws a contract exception directly, no need of 
// enumerating the returned enumerable
Test.Where(null, null);

推荐阅读
mobiledu2402851377
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有