8

次の2つの構文糖衣に感謝します。

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(構成時に取得、破棄時に解放)の読み取りロックの取得をカプセル化したクラスがある場合、インスタンスを構成するプロパティをクラスに作成できます。これにより、次のような構文になります。

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
} 

クロージャを使用したラムダ式のサポートにより、ラッパー関数を手動で作成したり、フィールドに必要なパラメーターを渡したりすることなく、任意の複雑なロジックが可能になるため、これは非常に強力です。

于 2010-05-21T14:53:47.823 に答える
1

いいえ、独自のキーワードを定義する方法はありません。これらは言語によって定義され、ILに解釈するためにコンパイラーに組み込まれています。私たちはまだその抽象化のレベルに到達していません!

varのようなものdynamicが紹介されたときに誰もがどれほど興奮するかを見てください。

そのことを念頭に置いて、投稿を編集して、ReaderWriterLockSlim例の構文をどのようにするかを示します。見ていて面白いと思います。

于 2010-05-21T14:37:51.460 に答える
1

Visual Studioの次のバージョンでは、この種の柔軟性に向けて大きな前進があることを理解しています。

.net Rocksで、Anders Hejlsbergは最近、次のVisual Studioリリース(2012?)の主要なテーマの1つが「サービスとしてのコンパイラ」になると述べ、それ以来、他の人々はこれが多くの扉を開くだろうとほのめかしました。言語拡張と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 に答える
0

他の人がすでに言っているように、コードにスコープIDisposableの概念を作成するために悪用される可能性があります。ネスティングもサポートできます。

于 2010-05-21T16:08:45.420 に答える
0

プリプロセッサディレクティブを探していると思いますが、C#はそれらをサポートしていません。それに最も近いのは、自分でデザインできるVisualStudioスニペットかもしれません。

于 2010-05-21T14:44:05.113 に答える