8

您将欣赏以下两种语法糖:

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 以及那些有回报的东西。但我相信你能想到一些事情。

4

10 回答 10

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 语句。由于闭包,您至少仍然可以使用局部变量。

于 2010-05-21T14:48:44.507 回答
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)
  }
 }
}

使用 then 会是这样的:

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

public void DoWork()
{....}

或者

lockObject.WithinLock(DoWork);

或者

lockObject.WithinLock(()=>
{
 DoWork();
 //DoOtherStuff
});
于 2010-05-21T14:48:19.453 回答
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 表达式的支持允许任意复杂的逻辑,而无需手动创建包装函数并在字段中传递所需的参数。

于 2010-05-21T14:53:47.823 回答
1

我知道下一版本的 Visual Studio 将大力推动这种灵活性。

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

现在你实际上可以用 DSL 做一些非常简洁的事情,包括创建你自己的关键字,尽管在我看来这仍然有点太尴尬。如果您有兴趣,请查看这篇关于在 Boo 中设计 DSL 的文章

它可能会让你大吃一惊。

于 2010-05-21T14:52:45.707 回答
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);
    }
  }
}
于 2010-05-21T14:57:42.957 回答
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. 无论哪种方式,我都可以接受。语义对我来说相当清楚 - 如果您开始某事,我希望您也完成某事。例如,如果您将“开始任务”消息写入日志文件,我也希望有“结束任务”日志消息。我只是比大多数开发人员对“释放资源”的定义更具包容性。

于 2010-05-21T15:40:29.443 回答
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
}
于 2010-05-28T19:49:52.850 回答
1

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

看看大家在介绍vardynamic介绍之类的东西时有多兴奋。

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

于 2010-05-21T14:37:51.460 回答
0

正如其他人已经说过的那样,IDisposable可以滥用在代码中创建范围的概念。它甚至可以支持嵌套。

于 2010-05-21T16:08:45.420 回答
0

我猜你会寻找预处理器指令,但 C#不支持那些. 最接近它的可能是Visual Studio 片段,您可以自己设计它。

于 2010-05-21T14:44:05.113 回答