为什么C#7丢弃标识符_仍在使用块中?

Ser*_*694 7 .net c# cil visual-studio c#-7.0

因此,我在使用UWP应用程序时经常使用的模式是使用SemaphoreSlim实例来避免竞争条件(我不喜欢使用lock它,因为它需要一个额外的目标对象,并且它不会异步锁定).

一个典型的代码片段如下所示:

private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);

public async Task FooAsync()
{
    await Semaphore.WaitAsync();
    // Do stuff here
    Semaphore.Release();
}
Run Code Online (Sandbox Code Playgroud)

随着更多的try/finally围绕整个事情块,如果之间的代码可能崩溃,但我想保持信号灯工作正常.

为了减少样板,我尝试编写一个包装类,它具有相同的行为(包括try/finally位),所需的代码更少.我也不想使用a delegate,因为每次都会创建一个对象,我只是想减少我的代码而不改变它的工作方式.

我想出了这个课程(为简洁起见,删除了评论):

public sealed class AsyncMutex
{
    private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);

    public async Task<IDisposable> Lock()
    {
        await Semaphore.WaitAsync().ConfigureAwait(false);
        return new _Lock(Semaphore);
    }

    private sealed class _Lock : IDisposable
    {
        private readonly SemaphoreSlim Semaphore;

        public _Lock(SemaphoreSlim semaphore) => Semaphore = semaphore;

        void IDisposable.Dispose() => Semaphore.Release();
    }
}
Run Code Online (Sandbox Code Playgroud)

它的工作方式是通过使用它你只需要以下内容:

private readonly AsyncMutex Mutex = new AsyncMutex();

public async Task FooAsync()
{
    using (_ = await Mutex.Lock())
    {
        // Do stuff here
    }
}
Run Code Online (Sandbox Code Playgroud)

一条线更短,try/finally内置(using块),真棒.

现在,尽管使用了discard运算符,但我不知道为什么会这样.

丢弃_实际上只是出于好奇,因为我知道我应该写的var _,因为我需要IDisposableusing块的末尾使用该对象,而不是丢弃.

但令我惊讶的是,两种方法都生成了相同的IL:

.method public hidebysig instance void T1() cil managed 
{
    .maxstack 1
    .locals init (
        [0] class System.Threading.Tasks.AsyncMutex mutex,
        [1] class System.IDisposable V_1
    )
    IL_0001: newobj       instance void System.Threading.Tasks.AsyncMutex::.ctor()
    IL_0006: stloc.0      // mutex

    IL_0007: ldloc.0      // mutex
    IL_0008: callvirt     instance class System.Threading.Tasks.Task`1<class System.IDisposable> System.Threading.Tasks.AsyncMutex::Lock()
    IL_000d: callvirt     instance !0/*class System.IDisposable*/ class System.Threading.Tasks.Task`1<class System.IDisposable>::get_Result()
    IL_0012: stloc.1      // V_1
    .try
    {
        // Do stuff here..
        IL_0025: leave.s      IL_0032
    }
    finally
    {
        IL_0027: ldloc.1      // V_1
        IL_0028: brfalse.s    IL_0031
        IL_002a: ldloc.1      // V_1
        IL_002b: callvirt     instance void System.IDisposable::Dispose()
        IL_0031: endfinally   
    }
    IL_0032: ret    
}
Run Code Online (Sandbox Code Playgroud)

"discarder" IDisposable存储在现场V_1并正确处理.

那么,为什么会这样呢?该文档不说与使用有关的丢弃操作什么using块,他们只是说,丢弃分配完全被忽略.

谢谢!

Waz*_*ner 7

using语句不需要显式声明局部变量.表达式也是允许的.

语言规范指定以下语法.

using_statement
    : 'using' '(' resource_acquisition ')' embedded_statement
    ;

resource_acquisition
    : local_variable_declaration
    | expression
    ;
Run Code Online (Sandbox Code Playgroud)

如果resource_acquisition的形式是local_variable_declaration,则local_variable_declaration的类型必须是动态的或可以隐式转换为的类型System.IDisposable.如果resource_acquisition的形式是表达式,则此表达式必须可隐式转换为System.IDisposable.

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/statements#the-using-statement

分配现有变量(或丢弃结果)也是一种表达方式.例如,以下代码编译:

var a = (_ = 10);
Run Code Online (Sandbox Code Playgroud)


Ser*_*rvy 5

丢弃功能的使用在这里真的是一个红鲱鱼。这样做的原因是因为该using语句可以接受解析为要处理的值的表达式(除了声明变量的替代语法)。此外,赋值运算符解析为分配的值

您在赋值运算符右侧提供的值是您的 Lock 对象,因此这就是表达式的_ = await Mutex.Lock()解析结果。由于该(不是作为变量声明,而是作为独立值)是一次性的,因此它将在using.