nganju
nganju

Reputation: 730

In C#, how do I keep certain method calls out of the codebase entirely?

I'm trying to get rid of all DateTime.Now method calls and replace them with my own GetNow() method, which may sometimes return a fixed date for testing purposes.

How can I enforce that no one adds a DateTime.Now call in the future? Can I use NDepend or StyleCop to check this on my continuous integration server?

Upvotes: 13

Views: 466

Answers (6)

Patrick from NDepend team
Patrick from NDepend team

Reputation: 13842

With NDepend it is very easy to write this rule:

// <Name>Forbid DateTime.Now, use xxx.GetNow() instead</Name>
WARN IF Count > 0 IN 
SELECT METHODS WHERE 
IsDirectlyUsing "OPTIONAL:System.DateTime.get_Now()"

Notice:

  • The prefix WARN IF Count > 0 IN that transforms the CQL query into a rule
  • The way the property is referenced through the string System.DateTime.get_Now()
  • The prefix OPTIONAL that means "Don't emit a compilation error if the property get_Now is not found". This makes sense since if your code doesn't use anymore get_Now(), it is not anymore referenced from NDepend analysis.

Also, to generate the original query...

SELECT METHODS WHERE 
IsDirectlyUsing "OPTIONAL:System.DateTime.get_Now()"

...just right-click DateTime.get_Now() and choose Select methods ... that are directly using me

enter image description here

Upvotes: 6

Dan Abramov
Dan Abramov

Reputation: 268255

Firstly, DateTime.Now is a property.
That means you don't need to put parenthesis after call.

Secondly, if by testing purposes you mean a framework like NUnit, you might want to check out Microsoft Moles which allows you to substitute any static method call with your own custom implementation while testing. Heck, it's cool:

[Test]
[ExpectedException (typeof (Y2KBugException))]
public void TestY2KBug ()
{
    MDateTime.NowGet = () => new DateTime (2001, 1, 1);
    Bomb.DetonateIfY2K ();
}


public static class Bomb {
    public static void DetonateIfY2K ()
    {
        if (DateTime.Now == new DateTime (2001, 1, 1))
            throw new Y2KBugException (); // take cover!
    }
}

Upvotes: 6

Randy Levy
Randy Levy

Reputation: 22655

One approach might be to add a pre-commit hook to your source control repository of choice to look for DateTime.Now and abort the check-in if you find the offending string. It's not foolproof and it might be annoying to your colleagues but it should help keep it out of the codebase.

Upvotes: 2

Mark H
Mark H

Reputation: 13897

You could use Moles in your tests to provide your own DateTime.Now when required, without the need to modify any existing code that calls it.

Another option might be to modify the assembly after compilation to call something else. (Perhaps, use Mono.Cecil to rewrite the IL, and add a command to the post-build in VS to run it.)

You could perhaps grab the Mono source and build yourself a custom mscorlib with the function removed.

Upvotes: 2

TehBoyan
TehBoyan

Reputation: 6890

I don't think that this is possible. What you are actually trying to do is override DateTime's struct functionality.

The only way I can think of is just to go with a custom class that will wrap common functionality of DateTime and offer different functionality on demand...

Upvotes: 0

Justin Niessner
Justin Niessner

Reputation: 245429

There's no real way to enforce something like this.

The closest you're going to come is making a custom FxCop rule or custom Visual Studio Code Analysis rule to error/warn on calls to DateTime.Now.

Upvotes: 3

Related Questions