7

I would like to write my own custom compound statements that have a similar mechanism to using and lock mechanisms where they have code injected at the beginning and end of the statement block before compilation.

I have tried searching for questions who may have asked similar questions but I could not properly figure out what this kind of "code scoping" is called, other than the documentation saying these are compound statements.

Im aware that "lock" and "using" are keywords. I dont want to have my own keywords as I know this is not possible.

Im not sure if this is possible in c# for example:

Rather than doing:

StartContext(8);
//make method calls
EndContext();

This could be reduced down to:

DoSomethingInContext(8) {
    //make method calls
}

Ofcourse this does not have to be just a single one line call. The start and ends of encapuslated code could be multiple lines of code thats is inserted by the custom compound statement.

Gelion
  • 501
  • 7
  • 14
  • 1
    At best you can do by either abusing `IDisposable` (though whether it *is* abuse is a matter of opinion), or using delegates, such as `DoSomethingInContext(8, () => { ... });` – Lasse V. Karlsen Feb 19 '18 at 09:59

2 Answers2

8

You can rewrite your code just slightly:

DoSomethingInContext(8, () => {
    // make method calls
});

The signature for your method would be something like this:

public void DoSomethingInContext(int contextId, Action contextBoundAction)
{
    // start/open/enter context
    try
    {
        contextBoundAction();
    }
    finally
    {
        // stop/close/exit context
    }
}

One thing to be aware of, since there is an alternative answer here that uses IDisposable, is that this delegate-based syntax can make intellisense in various (older) versions of Visual Studio and ReSharper get a little wonky. Sometimes it tries to help you fill out the parameter to DoSomethingInContext when you really want it to help you fill out parameters in method calls inside the delegate. This is also true for other IDEs such as older Xamarin Studios, which had severe performance problems regarding nested delegates (if you start nesting these context-bound things).

I wouldn't change my programming style because of this, but be aware of it.

Lasse V. Karlsen
  • 350,178
  • 94
  • 582
  • 779
  • Out of curiousity, when using this technique does the code encapsulated get unwrapped during compilation or is it preserved in the Action that is passed in? – Gelion Feb 19 '18 at 10:20
  • It gets rewritten to be an actual method on either the calling type or a separate class, depending on context, there are lots of gritty details. It does not get embedded into the DoSomethingInContext method though; something is passed in that references the method to call. – Lasse V. Karlsen Feb 19 '18 at 10:26
6

If you don't mind a minimal amount of additional code, you can re-use the using statement. Simply put your wrapper code in the constructor and Dispose method, e.g.:

public class MyWrapper: IDisposable
{
    int _id;

    public MyWrapper(int id)
    {
        _id = id;
        Debug.WriteLine("Begin " + _id);
    }

    public void Dispose()
    {
        Debug.WriteLine("End " + _id);
    }
}

Usage:

using(new MyWrapper(id))
{
    Debug.WriteLine("Middle " + id);
}

Demo on DotNetFiddle


I've used this method to wrap methods that need to be used together, even if something goes wrong (e.g. the Push and Pop methods of DrawingContext) - saves you a lot of finally blocks.

Manfred Radlwimmer
  • 12,469
  • 13
  • 47
  • 56
  • I can understand this mechanism although to me this seams a little "hacky" in abusing the disposal mechanism. – Gelion Feb 19 '18 at 10:10
  • This pattern is used in MVC `@using Html.BeginForm(...` – Hans Kesting Feb 19 '18 at 10:13
  • @Gelion It has worked **very** reliably for me so far. – Manfred Radlwimmer Feb 19 '18 at 10:17
  • Come to think of it, I can see myself actually using this for more instance based processes where I would need a multitude of method calls (of which may not necessarily be called in the same order) exposed to the context, rather than an "inline" process so to speak. +1 for this @Manfred. – Gelion Feb 19 '18 at 10:25
  • 1
    As I mentioned in my comment on the question, it's a matter of opinion of whether this is abusing `IDisposable` or not. Personally, I think this is fine, there's syntax in the language, and it would be rather silly not to exploit it, but lots of people get hung up on the dispose-unmanaged-things side of it. Best to make up your own mind about it. – Lasse V. Karlsen Feb 19 '18 at 10:28
  • One thing you need to be a lot more disciplined about is exceptions in the `Dispose` method, as these will effectively shadow any exception that occurs in the "make method calls" part inside, this has bitten me in the past. There *are* even more hackish ways of dealing with this but I would not advise it. You should by default make sure no exceptions can occur in the `Dispose` method. – Lasse V. Karlsen Feb 19 '18 at 10:29