Вы будете ценить следующие два синтаксического сахара:
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 и те виды вещей с возвратами. Но я уверен, что Вы могли думать о чем-то.
Не совсем так, но вы можете использовать делегат действия, чтобы получить что-то близкое:
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
Обратите внимание, что здесь есть некоторые ограничения. Например, у вас не может быть значимого оператора возврата внутри новых фигурных скобок. Благодаря закрытию вы, по крайней мере, по-прежнему сможете использовать локальные переменные.
Думаю, вам нужны директивы препроцессора, но C # не поддерживает их . Наиболее близкими к нему могут быть фрагменты кода Visual Studio , которые вы можете спроектировать самостоятельно .
Как уже было сказано другими, IDisposable
можно использовать для создания понятия области в вашем коде. Он даже может поддерживать вложение.
Нет, нет возможности определить свои собственные ключевые слова. Они определяются языком и встроены в компилятор для интерпретации в IL. Мы еще не достигли такого уровня абстракции!
Вы только посмотрите, как все приходят в восторг, когда появляются такие вещи, как var
и dynamic
.
Помня об этом, отредактируйте свое сообщение, чтобы показать желаемый синтаксис для примера ReaderWriterLockSlim
. Было бы интересно посмотреть.
Вы не можете определить такие конструкции напрямую, но есть способы создания подобных лаконичных паттернов:
Вы можете определить классы, реализующие IDisposable, чтобы инкапсулировать подобную семантику использования блоков. Например, если у вас есть класс, который инкапсулирует получение блокировки чтения ReaderWriterLockSlim (acquire on construct, release on 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
}
Это может быть довольно мощным, поскольку поддержка лямбда-выражений с замыканиями позволяет реализовать произвольно сложную логику без ручного создания функций-оберток и передачи необходимых параметров в полях.
Нет встроенной функции, которая делала бы то, что вы хотите. Однако вы можете использовать оператор 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);
}
}
}
Я понимаю, что в следующей версии Visual Studio будет большой толчок к такой гибкости.
Вкл.net rocks, Андерс Хейлсберг недавно сказал, что одной из основных тем следующего выпуска Visual Studio (2012?) будет «компилятор как услуга», и с тех пор некоторые другие люди намекнули, что это откроется множество дверей для расширения языка и более тесной интеграции с DSL (предметно-ориентированными языками).
Прямо сейчас вы действительно можете делать некоторые изящные вещи с помощью DSL, включая создание ваших собственных ключевых слов, хотя, на мой взгляд, это все еще слишком неудобно. Если вам интересно, ознакомьтесь с этой статьей о проектировании DSL в Boo .
Это может немного поразить вас.
Лично я злоупотребляю 1 , используя
для этого так, что у меня есть класс 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
Action:
using (new DisposeHelper(() => rw1.EnterReadLock(), () => rw1.ExitReadLock())) {
// do work
return value;
}
, но это просто уродливо.
1 : Технически я полагаю, что это больше злоупотребление IDisposable
, чем с использованием
. В конце концов, я на самом деле действительно хочу try / finally
- что дает мне с использованием
. Однако оказалось, что мне нужно реализовать IDisposable
, чтобы получить try / finally
.
В любом случае, меня это устраивает. Семантика для меня довольно ясна - если вы начнете что-то , я ожидаю, что вы закончите и что-то . Например, если вы напишете сообщение «Начало задачи» в файл журнала, я ожидаю также сообщения журнала «Завершение задачи». У меня просто более исчерпывающее определение «высвобождения ресурсов», чем у большинства разработчиков.
К сожалению, нет. Для поддержки этого компилятор C # должен быть более расширяемым для конечного пользователя. Это будет включать возможность определять свои собственные ключевые слова и иметь поддержку макросов и т. Д.
Однако вы можете создавать методы, которые хотя бы имеют немного похожее ощущение, например: (придуманный пример повторной реализации ключевого слова блокировки в коде пользователя)
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
});
Магический класс:
// 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
}