Эта функция существует? Определение моих собственных фигурных скобок в C # - PullRequest
8 голосов
/ 21 мая 2010

Вам понравятся следующие два синтаксических сахара:

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 ]

19 голосов
/ 21 мая 2010

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

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

5 голосов
/ 21 мая 2010

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

Тем не менее, вы можете создать методы сборки, которые, по крайней мере, выглядят немного похожими, например: (надуманный пример переопределения ключевого слова блокировки в коде пользователя)

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
});
3 голосов
/ 21 мая 2010

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

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

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

1 голос
/ 28 мая 2010

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

// 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
}
1 голос
/ 21 мая 2010

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

1 голос
/ 21 мая 2010

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

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

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

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

Это может немного поразить вас.

1 голос
/ 21 мая 2010

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

Просто посмотрите, как все взволнованы, когда появляются такие вещи, как var и dynamic.

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

0 голосов
/ 21 мая 2010

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

0 голосов
/ 21 мая 2010

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

...