c#中 "this "和 "ref "的等效功能。

问题描述 投票:0回答:1

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

/* 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 被使用,它在代码中与 Utf8JsonReaderMemory<T> 对象在嵌套函数之间传递。

我正在寻求重构(在这种情况下,在读者对象上使用扩展方法会好得多)。

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

更新 - ref this 需要?

作为更新,只要使用 this 失效,在 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()}");
}
c# .net this
1个回答
2
投票

ref 就可以了。而且... ref this 相当于另一种形式的

ExtensionsClass.ExpectNamedProperty(ref reader)

同时。请勿使用 in.

in 在这种情况下,会使性能变差。

in 对于 readonly struct而对于非readonly结构体,编译器会创建防御性副本 每次 的结构是用来确保该结构是只读的。这将大大降低性能。

在你的情况下。Utf8JsonReader 是一个 ref struct,不 readonly struct.

考虑这个例子。

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

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

ExpectWithRef(ref reader);
ExpectWithIn(reader);

汇编的IL ExpectWithRef:

// (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

汇编的IL: ExpectWithIn:

// (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 修饰符以及何时使用它。

这意味着你永远不应该在参数中传递一个非readonly结构。它几乎 始终会使性能变差.


4
投票

你可以写很多扩展方法,但它们真的有关系吗?把所有的东西都写在扩展方法中,会产生意大利面条代码。

我会选择 in 关键字。的进步。in 在...上 ref 是你不能修改参数,但你不会得到一个副本(就像一个 "普通 "参数)。你也可以将只读字段作为 in 参数。

private void Expect(in Utf8JsonReader reader, in JsonTokenType t)
{
   reader.Read();
    /* snip */
}
© www.soinside.com 2019 - 2024. All rights reserved.