views:

353

answers:

10

You'll appreciate the following two syntactic sugars:

lock(obj)
{
//Code
}

same as:

Monitor.Enter(obj)
try
{
//Code
}
finally
{
Monitor.Exit(obj)
}

and

using(var adapt = new adapter()){
//Code2
}

same as:

var adapt= new adapter()
try{
//Code2
}
finally{
adapt.Dispose()
}

Clearly the first example in each case is more readable. Is there a way to define this kind of thing myself, either in the C# language, or in the IDE? The reason I ask is that there are many similar usages (of the long kind) that would benefit from this, eg. if you're using ReaderWriterLockSlim, you want something pretty similar.

EDIT 1:

I've been asked to provide an example, so I'll give it a go:

myclass
{
ReaderWriterLockSlim rwl = new ReaderWriterLockSlim();

void MyConcurrentMethod()
{
  rwl.EnterReadLock();
  try{
    //Code to do in the lock, often just one line, but now its turned into 8!
  }
  finally
  {
    rwl.ExitReadLock();
  }
}
}

//I'd rather have:
void MyConcurrentMethod()
{
rwl.EnterReadLock()
{
   //Code block. Or even simpler, no brackets like one-line ifs and usings
}
}

Of course you'd have to give some thoughts as to how to use the TryEnterReadLocks and those kinds of things with returns. But I'm sure you could think of something.

+1  A: 

No, there is no way to define your own keywords. These are defined by the language and are built into the compiler to interpret to IL. We haven't quite reached that level of abstraction yet!

Just look at how excited everyone gets when things like var and dynamic are introduced.

With that in mind, edit your post to show what you'd like the syntax to be for the ReaderWriterLockSlim example. It'd be interesting to see.

David Neale
A: 

I guess you would be looking for preprocessor directives, but C# doesn't support those. The closest to it might be Visual Studio snippets, which you can design yourself.

ladenedge
+5  A: 

Unfortuanetly not. In order to support this the c# compiler would need to be more extensible for the end user. This would include being able to define your own keywords and have macro support etc...

However what you can do is build methods that at least have a bit of a similar feel such as this: (contrieved example of reimplementing lock keyword in user code)

public static class MyLocker
{
 public static void WithinLock(this object syncLock, Action action)
 {
  Monitor.Enter(syncLock)
  try
  {
   action();
  }
  finally
  {
   Monitor.Exit(syncLock)
  }
 }
}

using then would be like this:

object lockObject = new object();
MyLocker.WithinLock(lockObject, DoWork);

public void DoWork()
{....}

OR

lockObject.WithinLock(DoWork);

OR

lockObject.WithinLock(()=>
{
 DoWork();
 //DoOtherStuff
});
saret
+19  A: 

Not exactly, but you can use an action delegate to get something close:

void MyBrace(Action doSomething)
{      
     try
     {
        //wait for lock first

        doSomething();
     }
     finally
     {
         //special cleanup
     }
}

And use it like this:

MyBrace(() => 
{
   //your code goes here, but won't run until the lock is obtained
});  // cleanup will always run, even if your code throws an exception

Note that there are some limitations with this. You can't have a meaningful return statement inside the new braces, for example. Thanks to closures you will at least still be able to use local variables.

Joel Coehoorn
+1: That is actually pretty clever.
Brian Gideon
I like this, I might try to write this into an extension class for my rwlSlim usages.
Carlos
I use this pattern extensively, and it works really well. You can do stuff like `T WithTry<T>(Func<T> func, T valueIfFails)`
Isaac Cambron
+1  A: 

I understand there's going to be a big push towards that type of flexibility in the next version of Visual Studio.

On .net rocks, Anders Hejlsberg recently said that one of the main themes of the next Visual Studio release (2012?) will be "compiler as a service" and since then some other folks have hinted that this will open up a lot of doors for language extension and tighter integration with DSLs (Domain-Specific Languages).

Right now you can actually do some pretty neat stuff with DSLs including creating your own keywords, although it's still a little too awkward in my opinion. If you're interested, check out this article on designing DSLs in Boo.

It might blow your mind a little.

Brian MacKay
+3  A: 

You can't define constructs like this directly, but there are ways to create similar concise patterns:

You can define classes that implement IDisposable to encapsulate this sort of block usage semantics. For instance, if you had some class that encapsulated acquiring a ReaderWriterLockSlim (acquire on construct, release on Dispose) read lock, you could create a property on your class that constructs the instance, which results in a syntax like this:

using (this.ReadLock) // This constructs a new ReadLockHelper class, which acquires read lock
{
   //Do stuff here....
}
//After the using, the lock has been released.

This is arguably an abuse of IDisposable, but this pattern has definitely been used in production code.

You can use Aspect-oriented programming (with tools like PostSharp) to wrap a method body with reusable entry/exit logic. This is often used to inject logging or other cross-cutting concerns that you'd like to apply to your code without cluttering it up.

You can write functions that take delegates as parameters, which then wrap up the delegated logic in some similar structure. For instance, for the ReaderWriterLockSlim again, you can create a method like this:

private void InReadLock(Action action)
{
   //Acquires the lock and executes action within the lock context
} 

This can be quite powerful, as the support for lambda expression with closures allows for arbitrarily complex logic without manually creating wrapper functions and passing required parameters in fields.

Dan Bryant
I thought IDisposable was precisely for resources which require explicit release. Why do you think that not true of a lock?
Pete Kirkham
I personally think it's a reasonable use of the pattern, but there is an important difference. Most IDisposable classes also implement a finalizer for their unmanaged resources, which means the unmanaged resources will eventually be released. If you fail to Dispose an object that is holding a thread lock until Dispose is called, you've likely created deadlock. This is arguably more dangerous. There's also an extremely rare case in .NET 3.5 and earlier where Dispose will not be called if a ThreadAbortException is thrown at the right point.
Dan Bryant
@Dan Many IDisposable classes do not directly hold an unmanaged resource, so they don't have a finaliser. Your example is more reasonable than you suggest.
Jon Hanna
+1  A: 

There is no native feature that does what you want. However, you can exploit the using statement simply by implementing IDisposable.

public class Program
{
  private static SharedLock m_Lock = new SharedLock();

  public static void SomeThreadMethod()
  {
    using (m_Lock.Acquire())
    {
    }
  }
}

public sealed class SharedLock
{
  private Object m_LockObject = new Object();

  public SharedLock()
  {
  }

  public IDisposable Acquire()
  {
    return new LockToken(this);
  }

  private sealed class LockToken : IDisposable
  {
    private readonly SharedLock m_Parent;

    public LockToken(SharedLock parent)
    {
      m_Parent = parent;
      Monitor.Enter(parent.m_LockObject);
    }

    public void Dispose()
    {
      Monitor.Exit(m_Parent.m_LockObject);
    }
  }
}
Brian Gideon
+1  A: 

Personally, I abuse1 using so much for this that I have a DisposeHelper class for this:

class DisposeHelper : IDisposable {
    private Action OnDispose { get; set; }

    public DisposeHelper(Action onDispose) {
        this.OnDispose = onDispose;
    }

    public void Dispose() {
        if (this.OnDispose != null) this.OnDispose();
    }
}

That lets me return an IDisposable from an arbitrary method quite easily:

IDisposable LogAction(string message) {
    Logger.Write("Beginning " + message);
    return new DisposeHelper(() => Logger.Write("Ending " + message));
}

using (LogAction("Long task")) {
   Logger.Write("In long task");
}

You could also just do it inline:

rw1.EnterReadLock();
using (new DisposeHelper(() => rw1.ExitReadLock()) {
   // do work
   return value;
}

Or, with the addition of an onInit Action:

using (new DisposeHelper(() => rw1.EnterReadLock(), () => rw1.ExitReadLock())) {
   // do work
   return value;
}

but that's just ugly.

1: Technically, I suppose it's more of an abuse of IDisposable than it is of using. After all, I actually do want a try/finally - which is what using gives me. Turns out, though, that I have to implement IDisposable to get the try/finally. Either way, I'm okay with it. The semantics are fairly clear to me - if you start something, I expect you to finish the something as well. Eg., if you write a "Beginning task" message to the log file, I expect a "Ending task" log message as well. I just have a more inclusive definition of "releasing resources" than most devs.

Mark Brackett
A: 

As others have already said, IDisposable can be abused to create the notion of a scope in your code. It can even support nesting.

Jordão
+1  A: 

The magical class:

// Extend IDisposable for use with using
class AutoReaderWriterLockSlim : IDisposable {
    ReaderWriterLockSlim locker;
    bool disposed = false; // Do not unlock twice, prevent possible misuse
    // private constructor, this ain't the syntax we want.
    AutoReaderWriterLockSlim(ReaderWriterLockSlim locker) {
        this.locker = locker;
        locker.EnterReadLock();
    }
    void IDisposable.Dispose() { // Unlock when done
        if(disposed) return;
        disposed = true;
        locker.ExitReadLock();
    }
}
// The exposed API
public static class ReaderWriterLockSlimExtensions {
    public static IDisposable Auto(this ReaderWriterLockSlim locker) {
        return new AutoReaderWriterLockSlim(locker);
    }
}

Usage:

ReaderWriterLockSlim rwl = new ReaderWriterLockSlim();
using(rwl.Auto()) {
    // do stuff, locked
}
Dykam