2

In the current context we have two methods Start and Stop. These two methods are invoked from a function sequentially. There can be chances that a person invokes just Start() inside his method but forgets to invoke Stop(). e.g.

private void A()
{
  Start();

  //Buisness logic goes here

}

In this context when the code is compiled a warning or error needs to be displayed informing that for every Start() there should be a corresponding Stop(). Can somebody suggest ideas on how to go about implementing the same in C#?

The proper way of implementation would be

private void A()
{
  Start();

  //Buisness logic goes here

  Stop();

}
Prasad
  • 21
  • 2
  • 4
    Sounds like you should just implement the template method pattern instead. Or change `Start()` into `Execute(Action)`. – Jon Skeet Mar 21 '17 at 08:58
  • 2
    Make this class implement `IDisposable`, and call `Stop` in `Dispose` (or make `Start` return `IDisposable` and not class itself). There are different checks for `IDisposable` not being disposed in the scope. – Evk Mar 21 '17 at 08:58
  • So there is no possibility for Start and Stop to be called sequentially but from different methods? – Evk Mar 21 '17 at 09:15

3 Answers3

2

I would suggest you change your pattern to take care of the Start and Stop without ever exposing it to the programmer.

Change your class implementing Start & Stop to implementing an Execute method instead and dont even expose the Start & Stop.

public class MyClass
{
    private void Start(){} // old public method
    private void Stop(){} // old public method

    public void Execute(Action action)
    {
         Start();
         action();
         Stop();
    }
}

Usage:

var impl = new MyClass();
impl.Execute(() => {
   // do something in between start & stop
});
Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • This will require good amount of code change in the consumers. It should be a passive change. – Prasad Mar 21 '17 at 09:25
  • I know that wasn't a requirement from OP, but if the action throws an exception, `Stop()` won't be called. But I agree that this is harder to maliciously circumvent than my `using` solution. – Sentry Mar 21 '17 at 09:26
  • 2
    @Sentry good point - easy enough to fix using a `try..finally`. – Jamiec Mar 21 '17 at 09:40
1

Evk gave a good hint, here is how I would do it in more detail:

  1. Have a class (e.g. StartStop ) implement IDisposable

    public class StartStop : IDisposable
    {
        public StartStop() {  Start(); }
    
        public void Dispose() { Stop(); }
    
        protected void Start() { /*...*/ }
    
        protected void Stop() { /*...*/ }
    }
    
  2. Make use of this class with using:

    private void A()
    {
        using( var startStopCaller = new StartStopCaller() )
        {
            // Your code here
        }
     }
    

using will make sure Dispose() and subsequently Stop() will be called except for hard crashes.

Sentry
  • 4,102
  • 2
  • 30
  • 38
  • 1
    there's still nothing stopping a programmer just use `var startStop = new StartStop()` without it being inside a `using` and you're back to the original problem! – Jamiec Mar 21 '17 at 09:09
  • 1
    @Jamiec while true in general - many code inspection tools will produce a warning about that. – Evk Mar 21 '17 at 09:14
  • @Jamiec I agree, but nothing stops a programmer from using reflection to call private methods, so there will always be a way to circumvent safety measures – Sentry Mar 21 '17 at 09:23
  • This will require good amount of code change in the consumers. It should be a passive change. – Prasad Mar 21 '17 at 09:27
  • @Prasad I see. Then the answer of Arve might be a better approach. – Sentry Mar 21 '17 at 09:37
0

This can be approached in many ways, with two primary directions:

  • If you're using the later versions of the .NET platform, and thus the Roslyn compiler (Defaults from VS2015 and onwards), you can look into writing a compiler plugin that checks this for you. Here are some resources:
  • As some of the comments you got are pointing out, this could be fixed in your code and program design. This is most probably the "correct" way to approach this. Some examples:
    • Consider implementing IDisposable and use your class in a using statement - however, remember that stopping and disposing of an object might not be the same here. You should make an informed desicion about this with the knowledge you have about the inner workings of your program.
    • If you're calling these classes from elsewhere, you could let them implement an interface containing both your Start and Stop methods. And then let the calling class simply treat them as this interface, and make sure it calls both methods no matter which implementation it uses.
    • Re-architect your code to not depend upon running Start() and Stop() sequentially. This might require fundamental design changes to your program and how it works, but it might just be worth it. Both for readability and maintainability.
Community
  • 1
  • 1
Arve Systad
  • 5,471
  • 1
  • 32
  • 58
  • We also have consumers using vs 2012. Also am not sure how do we go about in this particular context. – Prasad Mar 21 '17 at 09:38
  • Then skip the compiler plugin part, and go for the code-wise redesign. That'll work regardless of VS version. – Arve Systad Mar 21 '17 at 11:47