C#: 我应该使用 out 还是 ref 来获取这个结构体返回?

4

我不确定提问的最佳方式,所以我将从一个例子开始:

public static void ConvertPoint(ref Point point, View fromView, View toView) {
    //Convert Point
}

这个调用是递归的。您传入一个点,它将其相对于fromView转换为相对于toView(只要其中一个是另一个的祖先)。
这个调用是递归的,逐级转换点。我知道,可变结构体很糟糕,但我使用可变点的原因是我只需要创建一个点并将其传递给递归调用,这就是我使用ref的原因。这样做是否正确,或者使用out参数、或者简单地声明该方法返回一个点会更好呢?
我不太熟悉在这些情况下如何处理结构体与类的区别。这是从Java移植过来的代码,显然点必须是一个类,因此重复使用相同的临时点比创建新点更有意义,而后者必须在每次调用时进行垃圾回收。
这可能是一个令人困惑的问题,为了增加一些混乱,当我在这里时,我应该保留一个临时静态Point实例以进行快速转换,还是每次调用这个方法时都创建一个新的点(它被频繁调用)?
5个回答

9

当处理 Point 等短寿命对象时,过度关注垃圾回收器是错误的,假设它实际上是一个类。考虑到这是 C#,它很可能是一个结构体,不超过 16 字节。在这种情况下,你应该始终编写方法以返回 Point。这在运行时进行了优化,结构体适合于 CPU 寄存器。

只有在结构体较大时才考虑通过 ref 传递结构体。


这听起来像是最简单的答案,也回答了我的问题,即为什么它应该只返回一个值,而在其他地方应该是一个引用(如果它很大)。谢谢! - Ed Marty
2
仅在结构体很大时才考虑通过引用传递结构体。那么,何谓“很大”? - Pharap

1

这取决于你在做什么以及为什么要这样做。

C#中的结构体与Java中的类完全不同。它们就像原始类型一样快速和语义化。实际上,原始类型在某种程度上也是结构体。传递它们并没有真正的成本,因此,如果这只是为了优化尚未出现问题的东西,我建议您只需使方法返回一个Point

然而,在某些情况下,传递结构体的副本可能会更慢,特别是当struct很大或者该方法是瓶颈时,可能因为您每秒钟调用它多次(我不认为这是这里的情况);在这些情况下,通过引用传递它们是有意义的,但首先您需要确定它是否是第一位的瓶颈。

例如,请注意,在Microsoft的XNA库中,Matrix结构体有两个相同方法的版本:

static void CreateLookAt(
ref Vector3 cameraPosition, ref Vector3 cameraTarget, ref Vector3 cameraUpVector,
    out Matrix result)

static Matrix CreateLookAt(
    Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector)

这只是出于性能原因,但仅因为该方法每秒被调用多次,而Vector3比普通的struct稍微大一些

那么你为什么要这样做呢?答案取决于你最初编写它的原因,但对于典型的代码来说,这并不重要。


1

我倾向于将所有东西都变成类来简化事情。如果您发现这会在应用程序中创建不必要的内存压力,那么只有在这种情况下才应该研究涉及可变结构的解决方案。

我之所以这样说,是因为GC往往非常擅长优化集合以满足您应用程序的需求,而可变结构则是一个臭名昭著的头疼问题。如果GC可以处理多个对象的内存压力,请不要引入这种问题。


它们并不是真正的头疼...它们只是可变结构体。 (?) - user541686
1
@Mehrdad - https://dev59.com/0XRC5IYBdhLWcg3wAcM3 - tvanfosson
@tvanfosson:我已经阅读了那个链接(以及类似的大量链接),但在我看来,它们夸大其词了... 我从来没有遇到过可变结构体的问题。 - user541686
@Mehrdad - 你也读了Lippert的博客吗?http://blogs.msdn.com/b/ericlippert/archive/2008/05/14/mutating-readonly-structs.aspx - tvanfosson
@tvanfosson: 不,我以前没有看过那篇文章。谢谢你提供链接 ;) ,但我仍然认为它们并不会带来麻烦——在第一次创建时就应该将不可变的对象设置为不可变状态,而且无论是使用类还是结构体,代码本身都应该非常清晰明了,是否有危险显然可以知道...一个“只读”变量意味着其内容应该保持不变(即使这是一个类,从技术上讲,它只是一个引用),因此如果你欺骗自己,那么结果可能是自己踩到了坑里,但这是你自己的责任。 - user541686
@Mehrdad - 我同意许多人对可变结构的危险进行了大量的抨击,但在某些情况下,它们确实具有一些非常不可预测的行为。那些你看不见的问题会在以后造成伤害,而且通常从代码中读不出来 :) - Andrew Hare

1

结构体应该是不可变的,因此我会建议从方法中返回一个不同的Point。如果您需要可变性,则应将其作为类并在类上创建一个方法进行转换。后者的副产品可能是将递归下降转换为迭代应用转换。


0

由于点是一个结构体,我认为在这里使用ref没有任何优势。它将在堆栈上被多次复制,所以为什么不直接返回它呢?如果您将此方法声明为返回类型为Point,我认为代码会更易读。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接