在谈论 C# 7.2 引用扩展方法时,"this ref" 和 "ref this" 有什么区别?

What is the difference between "this ref" and "ref this" when talking about C# 7.2 ref extension methods?

考虑以下扩展方法:

public static void Toggle(this ref bool @bool) => @bool = !@bool;

public static void Toggle2(ref this bool @bool) => @bool = !@bool;

这些只是切换一个 ref 布尔变量值。测试:

class Foo
{
    private bool _flag;
    public void DoWork()
    {
        _flag.Toggle();
        Console.WriteLine(_flag);
        _flag.Toggle2();
        Console.WriteLine(_flag);
    }
}

我们得到:

True
False

问题:选择一种或另一种语法是否存在任何隐藏的差异?

不,它们完全一样,就像(现在)您可以使用 $@@$.

编写内插的逐字字符串文字一样

没有区别。这些称为 修饰符 ,它们在规范中的顺序是 未定义

您可以在此处阅读 C# Language Specification 中关于方法参数的部分:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#method-parameters

它列出了不同的选项并定义了它们如何相互作用和混合,但没有说明必须使用的顺序。

尽管根据语言规范,它们完全相同。我发现了一个必须对编译器做更多事情的差异,这可能是一个编译器错误。重现此问题的设置是 Visual Studio 2019。该错误可能存在于其他编译器版本中,我真的没有对此进行更多研究。

如果您的语言水平为 7.2 或以上,则两种语法的工作方式相同。但是,如果您的项目使用的语言级别为 7.0 或 7.1,则将编译以下内容:

struct S {}

static class Extensions
{
    static void M(this ref S s) {}
}

但是如果我们像这里这样改变修饰符的顺序:

static class Extensions
{
    static void M(ref this S s) {}
}

您将收到以下编译器错误消息:

error CS8107: Feature 'ref extension methods' is not available in C# 7.0. Please use language version 7.2 or greater.

奇怪的是,虽然static void M(this ref S s)可以编译,但没有办法将方法作为S的扩展方法使用:

s.M() //error CS8107 here

但是您可以使用传统的静态语法调用该方法:

Extensions.M(ref s)

因此,尽管语言规范没有区别,但对于那些使用 Roslyn 编译器的人来说,最好使用 this ref 而不是 ref this,因为那样可以向后兼容 7.0 和 7.1只要错误存在 IMO

我报告了 here the lack of information about ref extensions methods for structs, and some other guy reported to Roslyn 这个问题。

我没有将这个答案标记为 THE answer,因为我认为 @Jon Skeet 和 @Joel Coehoorn 的那些在语言方面更准确。但我认为这无论如何都是有用的信息