2010-05-21 2 views
8

Вы оцените следующие два синтаксических сахара:Имеется ли эта особенность? Определение мои собственные фигурные скобки в 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, вам нужно что-то очень похожее.

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

+1

Вы можете эмулировать нагрузку вещей с помощью конструктора, как "пре-блок" и Dispose(), как «пост-блока ". – Dykam

+0

Можете ли вы пояснить, что вы подразумеваете под этим? – Carlos

+0

там вы: http://stackoverflow.com/questions/2882983/does-this-feature-exist-defining-my-own-curly-brackets-in-c/2932023#2932023 – Dykam

ответ

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 

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

+2

+1: Это на самом деле довольно умная. –

+0

Мне это нравится, я могу попытаться записать это в класс расширения для моих обычаев rwlSlim. – Carlos

+0

Я использую этот шаблон широко, и он работает очень хорошо. Вы можете делать такие вещи, как «T WithTry (Func func, T valueIfFails)' – 2010-05-28 19:59:09

1

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

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

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

5

Unfortuanetly нет. Чтобы поддержать это, компилятор 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() 
{....} 

ИЛИ

lockObject.WithinLock(DoWork); 

ИЛИ

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

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

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

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

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

3

Вы не можете определить конструкции, как это непосредственно, но есть способы, чтобы создать аналогичные краткие модели:

Вы можете определить классы, которые реализуют IDisposable инкапсулировать такого рода семантики использования блока. Например, если у вас был какой-то класс, который инкапсулировал получение ReadWriterLockSlim (приобретать при конструировании, релизе в 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, но этот шаблон определенно использовался в производственном коде.

Вы можете использовать Aspect-ориентированное программирование (с такими инструментами, как PostSharp), чтобы обернуть тело метода логикой входа/выхода многократного использования. Это часто используется для ввода журналов или других сквозных проблем, которые вы хотели бы применить к своему коду, не загромождая его.

Вы можете написать функции, которые принимают делегаты в качестве параметров, которые затем завершают делегированную логику в некоторой подобной структуре. Например, для ReaderWriterLockSlim снова, вы можете создать метод, как это:

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

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

+0

Я думал, что IDisposable предназначен именно для ресурсов, требующих явного выпуска. Почему вы думаете, что это не так? –

+0

Я лично считаю, что это разумное использование шаблона, но есть важное различие. Большинство классов IDisposable также реализуют финализатор для своих неуправляемых ресурсов, а это означает, что неуправляемые ресурсы в конечном итоге будут выпущены. Если вы не удалите объект, который удерживает блокировку потока, пока не вызывается Dispose, вы, вероятно, создали тупик. Это, возможно, более опасно. В .NET 3.5 и ранее был крайне редкий случай, когда Dispose не вызывается, если исключение ThreadAbortException выбрано в правой точке. –

+0

Многие классы IDisposable не имеют прямого хранения неуправляемого ресурса, поэтому у них нет финализатора.Ваш пример более разумен, чем вы предлагаете. –

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

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

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

Или, с добавлением onInit действий:

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

но это просто некрасиво.

: Технически, я полагаю, что это больше злоупотребление IDisposable чем из using. В конце концов, я на самом деле do хочу try/finally - вот что мне дает using. Оказывается, однако, что я должен реализовать IDisposable, чтобы получить try/finally. В любом случае, я в порядке. Семантика для меня довольно понятна - если вы начинаете что-то, я ожидаю, что вы закончите что-то. Например, если вы пишете сообщение «Начало задачи» в файл журнала, я ожидаю также сообщение журнала «Конечная задача». У меня есть более исчерпывающее определение «освобождение ресурсов», чем большинство разработчиков.

0

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

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 
}