如何使用 in 参数直接调用带有 ref 参数的方法

Posted

技术标签:

【中文标题】如何使用 in 参数直接调用带有 ref 参数的方法【英文标题】:How to directly call a method with ref parameter using in parameter 【发布时间】:2019-05-07 04:44:50 【问题描述】:

我正在使用旧库中的方法:F (ref T t) where T: struct。仅出于性能原因将其声明为ref,并且不会修改它接收到的数据。此库无法更改。

在我的代码中,我有一个新方法 G (in T t) where T: struct 调用 F

有没有办法直接用我收到的参考资料调用F,而无需先将其复制到临时文件中?

【问题讨论】:

方法是让你的函数也成为一个参考函数。然后你就一直把参考递过来。 In、ref 和 out 只是它们的意图声明不同:“in 关键字导致参数通过引用传递。它就像 ref 或 out 关键字,只是 in 参数不能被被调用的方法。虽然可以修改 ref 参数,但 out 参数必须由被调用的方法修改,并且这些修改在调用上下文中是可观察的。 in 将一直是正确的工具。但如果缺少这一点,您可能也必须使用 ref 。编译器不允许您在 ref 参数点上放置 in 参数,这正是 in 存在的原因。 【参考方案1】:

我刚刚读到“in”。显然,in、ref 和 out 都是引用调用的一种形式。然而,主要区别在于“他们的意图声明”。

in 参数无法修改。它们只是出于性能原因的参考,如果有的话。

ref 参数可能会或可能不会被修改。它最接近经典的“引用调用”。事实上,这是您通过裸指针实现它时获得的唯一方法

out 参数需要修改;如果一个值需要初始化(如构造函数中的只读),这可能是相关的 - 如果它作为输出变量提交,编译器可以放心设置。其他函数编译器确保了这一点。

在某种程度上in 是一种“参考,但只读”。无法将您收到的“输入”内容分配给“参考”或“输出”,并阻止您意外更改值。我想你也不会在那里使用 ref (这使得这可能毫无意义)或复制。

【讨论】:

对不起,这是一个通用的答案,不是很有帮助。我知道关键字的作用。我正在寻找一种规避正常操作的方法,希望通过一些巧妙的类型转换或类似的方法而不是使用 Emit 来这样做(这是我目前正在做的)。【参考方案2】:

是的,有办法(但它使用了不安全的黑魔法)。

首先是免责声明。

F 方法不修改struct 的事实只是您的“约定”。对于 C# 编译器,ref 提供的 struct 是完全可变的。

拥有一个由readonly ref 通过in 提供的struct 告诉编译器:请确保这个struct 不能被变异。

顺便说一句,如果您将struct 传递为in,您必须确保此struct 被声明为readonly struct。否则,编译器将创建struct 的防御性副本(详情请阅读here。)这是您通常无法将readonly struct 引用传递给接受@ 的方法的第二个原因987654337@ ref 并对其进行变异。

如果您仍想解决所有这些限制,可以使用System.Runtime.CompilerServices.Unsafe NuGet 包。

Unsafe静态类中有一个方法可以帮助你:

public static ref T AsRef<T>(in T source);

这是一个例子:

void F<T>(ref T t) where T : struct



void G<T>(in T t) where T : struct

    F(ref System.Runtime.CompilerServices.Unsafe.AsRef(in t));

【讨论】:

我会检查一下包裹,谢谢。然而,这是不正确的:“不能将只读结构引用传递给通过 ref 接受结构的方法”。你可以做到,而且不需要防御性副本,因为你实际上不能在 ref T 方法中修改它,因为它是只读的。 @chase,不愉快的措辞,精致。我的意思是:如果你通过in 获得它,那么它可能是readonly,因此将它传递给接受ref 的方法可能很危险,因为该方法可能会改变struct 这确实是一个非常有用的包。 AsRef 的性能似乎与直接调用相同。另外,不要再发射了,耶! 我想我的“不能真正修改它”也可以使用免责声明:不是通过传统方式。随着不安全和反思以及诸如此类的一切都消失了,正如这个包精彩地展示的那样;)

以上是关于如何使用 in 参数直接调用带有 ref 参数的方法的主要内容,如果未能解决你的问题,请参考以下文章

无法通过 std::ref() 调用带有 auto& 参数的 std::invoke()

如果“错误 CS1628:无法在匿名方法、lambda 或查询表达式中使用 in ref 或 out 参数”,如何在线程中使用 ref 参数?

使用自定义 ItemReader 调用带有 IN 和 OUT 参数的存储过程

C# 中“in”参数的用途

如何使用ref参数调用方法时删除ref关键字限制?

ref 与 out