为什么将func <t>传递给构造函数而不是T?

时间:2017-07-28 12:56:46

标签: c# linq func

我遇到了这个问题about dealing with DateTime.Now in unit tests的接受答案,其中包含以下代码示例:

private readonly Func<DateTime> _nowProvider;
public SomeClass(Func<DateTime> nowProvider)
{
    _nowProvider = nowProvider;
}

public bool Foo()
{
    return (_nowProvider().DayOfWeek == DayOfWeek.Sunday);
}

实例化:

var s = new SomeClass(() => DateTime.Now);

我在C#中使用Func<T>的时间并不多,所以我想我会看一下at the Microsoft documentation for it,其中有以下几点:

  

您可以使用此委托来表示可以作为参数传递的方法,而无需显式声明自定义委托。封装的方法必须对应于此委托定义的方法签名。这意味着封装的方法必须没有参数,并且必须返回一个值。

为什么在示例中传递Func<DateTime>,将Class(() => DateTime.Now)实例化为构造函数

更为有益?

而不是简单地将实例化为DateTime的{​​{1}}参数传递给构造函数?

根据上面提到的Microsoft文档,LINQ lambda构造函数也接受Class(DateTime.Now)个参数,我对这些参数的经验证明它们非常灵活,但我无法理解为什么?

3 个答案:

答案 0 :(得分:6)

  

而不是简单地将实例化为Class(DateTime.Now)的DateTime参数传递给构造函数?

因为该值应该是当前的DateTime而不是该类已实例化时的值。

当代码运行时,Func将返回完全代码执行时的日期

如果DateTime将存储在一个字段中,那么它将是创建时间,而不是现在。

我有一个例子。

假设您在星期六23:59:55创建Class的实例。

10秒后,以下剪辑:

(passedDateTime.DayOfWeek == DayOfWeek.Sunday); 

会返回false。

使用提供程序时,日期时间实际上是星期日 - 它执行的时间。

技术:

DateTime是一个结构。

将DateTime作为参数传递给方法或构造函数时,它将作为值传递,而不是作为引用传递。

因此DateTime不会是最新的,而只是值的快照。

您可以自己确认:

var dateTime = DateTime.Now;

System.Threading.Sleep(1000);
bool equals = dateTime == DateTime.Now; // false

答案 1 :(得分:3)

此模式允许在正常操作期间由DateTime.Now提供日期和时间,或在单元测试期间进行严密控制。

例如,想要测试基于时间的功能的单元测试可以在每次调用(一种常见的缓存技术)之间调用两次超过5分钟的函数时验证返回的结果是否正确,而无需等待5电话之间的分钟数。

这也是&#34;控制反转的一个例子。图案。检索数据的方法是注入&#34;在课堂上,通常是通过构造函数。然后,该类可以自由地使用注入的任何方法而不了解其实现。

答案 2 :(得分:2)

我附上了一个小例子,说明在单元测试中它的样子。

如果你没有能力提供不同的&#34;现在&#34;单元测试的运行时间会有所不同。

    [TestMethod]
    public void TestFoo()
    {
        var obj = new SomeClass(() => DateTime.Now);

        //Only true on sundays
        Assert.IsTrue(obj.Foo());

        //This is sunday
        obj = new SomeClass(() => new DateTime(2017, 7, 30));
        //This will be always true
        Assert.IsTrue(obj.Foo());

        //This is not sunday
        obj = new SomeClass(() => new DateTime(2017, 7, 29));
        //This will be always false
        Assert.IsFalse(obj.Foo());
    }