Существует ли эта особенность? Определение моих собственных фигурных скобок в C#



Вы оцените следующие два синтаксических сахара:



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 и подобные вещи с возвратами. Но я уверен, что вы могли бы что-нибудь придумать.

662   10  

10 ответов:

Не совсем, но вы можете использовать делегат действия, чтобы получить что-то близкое:

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. Благодаря закрытиям вы, по крайней мере, все еще сможете использовать локальные переменные.

К сожалению, нет. Для поддержки этого компилятор c# должен быть более расширяемым для конечного пользователя. Это будет включать в себя возможность определять свои собственные ключевые слова и иметь поддержку макросов и т. д...

Однако то, что вы можете сделать, это построить методы, которые, по крайней мере, имеют немного похожее чувство, такое как это: (contrieved пример переопределения ключевого слова 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()
{....}

Или

lockObject.WithinLock(DoWork);

Или

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

Вы не можете определить такие конструкции напрямую, но есть способы создать похожие сжатые шаблоны:

Можно определить классы, реализующие 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
} 

Это может быть довольно мощным, так как поддержка лямбда-выражения с замыканиями позволяет осуществлять сколь угодно сложную логику без ручного создания функций-оболочек и передачи требуемых параметров в поля.

Нет, нет способа определить свои собственные ключевые слова. Они определяются языком и встроены в компилятор для интерпретации в IL. Мы еще не достигли такого уровня абстракции!

Просто посмотрите, как все возбуждаются, когда вводятся такие вещи, как var и dynamic.

Имея это в виду, отредактируйте свой пост, чтобы показать, каким должен быть синтаксис для примера ReaderWriterLockSlim. Было бы интересно посмотреть.

Нет ни одной родной функции, которая делает то, что вы хотите. Однако вы можете использовать оператор 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);
    }
  }
}

Лично я злоупотребляю1using так много для этого, что у меня есть класс 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");
}

Вы также можете просто сделать это inline:

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. В любом случае, я не против. Семантика довольно ясна для меня - если вы начинаете что-то, я ожидаю, что вы закончите что-то. Напр.., если вы пишете сообщение " начальная задача "в файл журнала, я ожидаю, что сообщение журнала" конечная задача " также появится. У меня просто есть более широкое определение "высвобождения ресурсов", чем у большинства разработчиков.

Я понимаю, что в следующей версии Visual Studio будет большой толчок к такому типу гибкости.

На .net rocks, Андерс Хейльсберг недавно сказал, что одна из главных тем следующего выпуска Visual Studio (2012?) будет "компилятор как сервис", и с тех пор некоторые другие люди намекали, что это откроет много дверей для расширения языка и более тесной интеграции с DSL (доменными языками).

Прямо сейчас вы можете на самом деле сделайте некоторые довольно аккуратные вещи с DSL, включая создание собственных ключевых слов, хотя это все еще немного неудобно, на мой взгляд. Если вам интересно, ознакомьтесь сэтой статьей о проектировании DSL в Boo .

Это может немного взорвать ваш разум.

Магический класс:

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

Я думаю, вы будете искать директивы препроцессора, но C# не поддерживает эти. Наиболее близкими к нему могут быть фрагменты Visual Studio, которые вы можете спроектировать самостоятельно.

Как уже говорили другие, IDisposable можно использовать для создания понятия области в вашем коде. Он может даже поддерживать вложенность.

Comments

    Ничего не найдено.