C# c中'this'和'ref'的等效功能#

C# c中'this'和'ref'的等效功能#,c#,.net,this,C#,.net,This,如果我在一个类中有一个函数: /* class snipped */ private void Expect(ref Utf8JsonReader reader, JsonTokenType t) { reader.Read(); /* snip */ } 这与静态辅助函数有什么不同吗 /*static class snipped*/ static public void Expect(this Utf8JsonReader reader, JsonTokenType t) {

如果我在一个类中有一个函数:

/* class snipped */
private void Expect(ref Utf8JsonReader reader, JsonTokenType t)
{
    reader.Read();
    /* snip */
}
这与静态辅助函数有什么不同吗

/*static class snipped*/
static public void Expect(this Utf8JsonReader reader, JsonTokenType t)
{
    reader.Read();
    /* snip */
}

// call helper via reader.Expect(requiredToken)
如果使用
ref
时出现任何看不见的细微差别,我会问,它与嵌套函数之间传递的
Utf8JsonReader
Memory
对象一起大量存在于代码中

我希望重构(在本例中,在reader对象上使用扩展方法会更好)

this
(外部类上的扩展方法)和
ref
(函数之间的传递引用)在功能上是否等效

更新-
引用此
必需???

作为更新,仅使用
不起作用,在
ExpectNamedProperty
函数中,它将调用
reader。Expect
,但返回对象时将还原。不知何故,在堆栈上复制了一个副本,或者发生了什么事情

我甚至不知道这是一个有效的组合,
ref this
可以工作,而
this
只能不修改。需要澄清的是不要做可怕的事情

public static void Expect(ref this Utf8JsonReader reader, JsonTokenType t)
{
    reader.Read(); // this mutation is never passed back to caller      
}

public static void ExpectNamedProperty(ref this Utf8JsonReader reader, string expectedPropertyName)
{
    reader.Expect(JsonTokenType.PropertyName, expectedPropertyName);

    // at this point it is as if the function above was never called

    var foundPropertyName = reader.GetString();

    if (foundPropertyName != StreamFieldNames.Event)
        throw new JsonException($"expected {StreamFieldNames.Event} found {foundPropertyName} at position {reader.Position.GetInteger()}");
}

您可以编写许多扩展方法,但它们真的相关吗?用扩展方法编写每一件事都会生成意大利面代码

我会在关键字中选择
。与
ref
相比,
中的
的进步在于,您无法修改参数,但无法获得副本(如“正常”参数)。您还可以在
参数中作为
传递只读字段

private void Expect(in Utf8JsonReader reader, in JsonTokenType t)
{
   reader.Read();
    /* snip */
}

ref
正常。而
ref此
相当于/另一种形式的

ExtensionClass.ExpectNamedProperty(参考读取器)

同时,不要在
中使用
。
在这种情况下,
中的
会降低性能

中的
适用于
只读结构
,而对于非只读结构,编译器每次使用该结构时都会创建防御副本,以确保该结构是只读的。这会显著降低性能

在您的例子中,是
ref结构
,而不是
只读结构

考虑这个例子:

private void ExpectWithIn(in Utf8JsonReader reader)
{
    reader.Read();
}

private void ExpectWithRef(ref Utf8JsonReader reader)
{
    reader.Read();
}

ExpectWithRef(ref reader);
ExpectWithIn(reader);
ExpectWithRef的编译IL

// (no C# code)
IL_0000: nop
// reader.Read();
IL_0001: ldarg.1
IL_0002: call instance bool [System.Text.Json]System.Text.Json.Utf8JsonReader::Read()
IL_0007: pop
// (no C# code)
IL_0008: ret
ExpectWithIn
的编译IL:

// (no C# code)
IL_0000: nop

// The compiler creates defensive copy to make sure reader variable is readonly
// The compiler repeats this for every use of reader variable
// so this is significant impact 

// Utf8JsonReader utf8JsonReader = reader;
IL_0001: ldarg.1
IL_0002: ldobj [System.Text.Json]System.Text.Json.Utf8JsonReader
IL_0007: stloc.0


// utf8JsonReader.Read();
IL_0008: ldloca.s 0
IL_000a: call instance bool [System.Text.Json]System.Text.Json.Utf8JsonReader::Read()
IL_000f: pop
// (no C# code)
IL_0010: ret
Sergey Tepliakov解释了
修饰符中的
,以及何时使用它

这意味着您不应将非只读结构作为in参数传递。它几乎总是会使性能变差


使得
Expect
成为
Utf8JsonReader
的扩展方法:您可以将
reader.Expect(t)那么。也许我误解了,但在第一个示例中,您是否需要
ref
?如果您打算更改
读取器本身的引用,是否真的需要这样做?(即reader=)@code陌生人这是一个结构,如果他使用
System.Text.Json.Utf8JsonReader
,我会将它写为
private void Expect(在Utf8JsonReader中,在JsonTokenType t中)
,这与
ref
@code陌生人相同,我认为它是一个类,在这里也是一样。。我得先查一下,从来没用过。你为什么要用
ref this
?正如其他人提到的,
这使得它成为一种扩展方法。将其标记为
ref
允许您更改指向的对象,这严重违反了for-an-extension方法。您可以同时执行
Expect(这在Utf8JsonReader,JsonTokenType t中)
就像我说的,它真的与Utf8JsonReader密切相关吗?代码中的许多点都使用它吗?将它用作扩展方法的一个问题是,它不再支持继承。你不能推翻它。是的,我很感激这一点。有很多期望值略有不同,它们占据了全班的大部分。在这种情况下,将它们放在扩展方法中会使实际使用扩展的类更加清晰。
in
不能在
Utf8JsonReader
上使用。我发布了一个答案来解释原因。在这种情况下,部分类是相关的,因为使用ref和这一起需要修改项目(这是必需的),但其他任何开发人员都不清楚,因此是危险的。分部类至少使它明确地清除了整个
中的
关键字只不过是一个伪解决方案,而不是一个实体“不创建副本”并通过引用传递。只读。我在sharplab上做了一些测试,我在调用方法时看到了防御副本。只有在访问这些字段时,才会创建副本。