这个功能是否存在?在C#中定义自己的花括号

时间:2010-05-21 14:34:00

标签: c# .net readability syntactic-sugar

你会欣赏以下两种句法糖:

lock(obj)
{
//Code
}

same as:

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

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

same as:

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

显然,每种情况下的第一个例子都更具可读性。有没有办法自己定义这种东西,无论是在C#语言中,还是在IDE中?我问的原因是,有很多类似的用法(长篇)可以从中受益,例如。如果您使用的是ReaderWriterLockSlim,那么您需要一些非常相似的东西。

编辑1:

我被要求提供一个例子,所以我会试一试:

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
}
}

当然,你必须考虑一下如何使用TryEnterReadLocks和那些带回报的东西。但我相信你能想到什么。

10 个答案:

答案 0 :(得分:19)

不完全是,但你可以使用一个动作委托来取得成果:

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

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

并像这样使用它:

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

请注意,这有一些限制。例如,在新大括号内不能有有意义的return语句。多亏了闭包,你至少仍然可以使用局部变量。

答案 1 :(得分:5)

不幸的是没有。为了支持这一点,c#编译器需要对最终用户更具可扩展性。这包括能够定义自己的关键字并获得宏支持等......

然而,你可以做的是构建方法,至少有一些类似的感觉,如下所示: (在用户代码中重新实现lock关键字的相关示例)

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

然后使用就像这样:

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

public void DoWork()
{....}

OR

lockObject.WithinLock(DoWork);

OR

lockObject.WithinLock(()=>
{
 DoWork();
 //DoOtherStuff
});

答案 2 :(得分:3)

你不能直接定义这样的结构,但有一些方法可以创建类似的简洁模式:

您可以定义实现IDisposable的类来封装这种块使用语义。例如,如果你有一些类封装获取ReaderWriterLockSlim(在构造上获取,在Dispose上发布)读取锁,你可以在你的类上创建一个构造实例的属性,这会产生如下语法:

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

这可以说是对IDisposable的滥用,但这种模式肯定已用于生产代码中。

您可以使用面向方面的编程(使用PostSharp之类的工具)来包装具有可重用入口/出口逻辑的方法体。这通常用于注入您希望应用于代码的日志记录或其他横切关注点而不会使其混乱。

您可以编写将委托作为参数的函数,然后将委托逻辑包装在一些类似的结构中。例如,对于ReaderWriterLockSlim,您可以创建一个这样的方法:

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

这可能非常强大,因为对带有闭包的lambda表达式的支持允许任意复杂的逻辑,而无需手动创建包装函数并在字段中传递所需的参数。

答案 3 :(得分:1)

不,没有办法定义自己的关键字。这些由语言定义,并内置到编译器中以解释为IL。我们还没有达到那种抽象水平!

看看每个人在vardynamic之类的介绍时会有多激动。

考虑到这一点,请编辑您的帖子,以显示您希望ReaderWriterLockSlim示例的语法。看到它会很有趣。

答案 4 :(得分:1)

据我所知,在下一版本的Visual Studio中,这种灵活性将会有很大的推动力。

在.net上,Anders Hejlsberg最近说下一个Visual Studio版本(2012年?)的主题之一将是“编译器即服务”,从那以后其他一些人暗示这将是为语言扩展和与DSL(域特定语言)的更紧密集成打开了很多大门。

现在你可以用DSL做一些非常好的东西,包括创建你自己的关键词,虽然在我看来它仍然有点太尴尬。如果您有兴趣,请查看this article on designing DSLs in Boo

这可能会让你大吃一惊。

答案 5 :(得分:1)

没有原生功能可以满足您的需求。但是,您只需实施using即可利用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);
    }
  }
}

答案 6 :(得分:1)

就我个人而言,我滥用 1 using这么多,我有一个DisposeHelper类:

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

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

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

这让我可以很容易地从任意方法返回IDisposable

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

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

您也可以直接进行内联:

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

或者,添加onInit动作:

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

但那只是丑陋。

1 :从技术上讲,我认为这更像是IDisposable的滥用而不是using的滥用。毕竟,我实际上想要一个try/finally - 这是using给我的。但事实证明,我必须实施IDisposable才能获得try/finally。 无论哪种方式,我都没关系。语义对我来说相当清楚 - 如果你开始某些东西,我希望你也完成某些东西。例如,如果您将“开始任务”消息写入日志文件,我期望还有一个“结束任务”日志消息。与大多数开发者相比,我对“释放资源”的定义更具包容性。

答案 7 :(得分:1)

神奇的课程:

// 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);
    }
}

用法:

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

答案 8 :(得分:0)

我猜你会寻找预处理器指令,但是C#doesn't support those。距离它最近的可能是Visual Studio snippets,您可以design yourself

答案 9 :(得分:0)

正如其他人已经说过的那样,IDisposable可能会被滥用来在您的代码中创建notion of a scope。它甚至可以支持嵌套。

相关问题