Why pass func <T> to a constructor and not T?
I came across the accepted answer to this question about working with DateTime.Now in unit tests , which contains the following code example:
private readonly Func<DateTime> _nowProvider;
public SomeClass(Func<DateTime> nowProvider)
{
_nowProvider = nowProvider;
}
public bool Foo()
{
return (_nowProvider().DayOfWeek == DayOfWeek.Sunday);
}
Initiated as such:
var s = new SomeClass(() => DateTime.Now);
I have not used it Func<T>
in C #, so I thought I would look in the Microsoft documentation for it has the following notes:
You can use this delegate to represent a method that can be passed as a parameter without explicitly declaring a custom delegate. The encapsulated method must match the method signature that is defined by this delegate. This means that the encapsulated method must not have any parameters and must return a value.
Why would it be more useful in this example to pass Func<DateTime>
created as Class(() => DateTime.Now)
constructor
Instead of just passing a parameter DateTime
created as a Class(DateTime.Now)
constructor?
According to the Microsoft documentation mentioned above, LINQ lambda constructors also take arguments Func<T>
, and my experience with that is that they are extremely flexible, but I can't figure out why?
source to share
Instead of just passing in the constructor a DateTime parameter generated as a class (DateTime.Now)?
Because the value must be the current DateTime, not when the class was instantiated.
When the code runs, Func returns the date exactly when the code is executed.
If the DateTime will be stored in a field, it will be the creation time, not now.
I have an example.
Let's say you instantiate Class
at 23:59:55 on Saturday.
After 10 seconds the following shots:
(passedDateTime.DayOfWeek == DayOfWeek.Sunday);
will return false.
With the provider, the datetime is actually on Sunday - the time it runs.
Technical:
DateTime is a structure.
When you pass a DateTime to a method or constructor as a parameter, it is passed as a value, not a reference.
So it DateTime
won't be updated, but just a snapshot of the value.
You can confirm this yourself:
var dateTime = DateTime.Now;
System.Threading.Sleep(1000);
bool equals = dateTime == DateTime.Now; // false
source to share
This template allows the Date and Time to be supplied either DateTime.Now
during normal operation or closely monitored during unit testing.
For example, a Unit Test that wants to test transient functionality can verify that the returned result is correct when the function is called twice with more than 5 minutes between each call (the usual way of caching), without having to wait 5 minutes between calls.
This is also an example of a "Control Call" template. When a getter is "injected" into a class, usually through a constructor. The class is then free to use any method that was injected without knowing its implementation.
source to share
I have attached a small example of what this might look like in a unit test.
Unless you are able to provide a different Now, the unit test will differ depending on the time it is run.
[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());
}
source to share