是否更好地使用多个输出值或返回组合值类型?

时间:2011-03-10 21:18:48

标签: c# .net performance clarity

例如,沿着:

public bool Intersect (Ray ray, out float distance, out Vector3 normal)
{

}

VS

public IntersectResult Intersect (Ray ray)
{

}

public class IntersectResult
{
    public bool Intersects {get;set;}
    public float Distance {get;set;}
    public Vector3 Normal {get;set;}
}

对于清晰度,易用性以及最重要的性能而言,哪个更好。

8 个答案:

答案 0 :(得分:16)

我会使用组合类型,我会告诉你原因:因为值的计算应该返回值,而不是改变一堆变量。一旦你需要多个变量突变,变异就不会扩展。假设你想要上千件事:

IEnumerable<Ray> rays = GetAThousandRays();
var intersections = from ray in rays 
                    where Intersect(ray, out distance, out normal)
                    orderby distance ...

执行查询现在重复改变相同的两个变量。您根据正在变异的值进行排序。这是一团糟。不要做出改变事物的查询;这很令人困惑。

你想要的是:

var intersections = from ray in rays 
                    let intersection = Intersect(ray)
                    where intersection.Intersects
                    orderby intersection.Distance ...

没有突变;将一系列值操作为值而不是作为变量

我也倾向于摆脱那个布尔标志,并使值成为一个不可变的结构:

// returns null if there is no intersection
Intersection? Intersect(Ray ray) { ... }

struct Intersection 
{
    public double Distance { get; private set; }
    public Vector3 Normal { get; private set; }
    public Intersection(double distance, Vector3 normal) : this()
    {
        this.Normal = normal;
        this.Distance = distance;
    }
} 

答案 1 :(得分:10)

我会使用组合类型。

使用对象,您可以附加行为,并返回任意复杂的对象。您可能希望将来重构您的方法,并更改返回值。通过将它们包装在返回对象中并向该对象添加行为,这种重构可以变得非常透明。

使用元组之类的东西很诱人。然而,重构工作在一段时间后变得令人头疼(我从这里的经验来讲,刚刚犯了这个错误再次

答案 2 :(得分:6)

最好返回组合类型。至少你的方法签名更清晰,程序员在调用时工作的工作量更少。 EG:您不必声明和初始化变量,这会使调用代码混乱。

答案 3 :(得分:5)

有些人说这是一个偏好问题,但我认为返回一个复杂的对象更好,不仅是为了清晰,而且是为了维护。

如果您需要添加另一位数据作为输出,除了添加额外响应的代码之外,您还必须更改方法签名;复杂的输出类型不是这样。此外,模拟(用于单元测试)输出参数更难。它似乎打破了“简单”的理念 - 当你只需要一个输入时,你必须经历设置输出参数的麻烦。 OOP语言的优势在于制作类型 - 在我看来,这条路线也是如此。

两者之间的性能差异可以忽略不计。

答案 4 :(得分:3)

您可以使用元组而不是创建IntersectResult类作为替代。

参考:

http://msdn.microsoft.com/en-us/library/system.tuple.aspx

但是,我绝对倾向于将复杂类型返回到超出参数。

答案 5 :(得分:3)

仅供记录: 很难找到魔术三重奏的一个很好的例子(清晰度,易用性和性能)。

显然,第二个示例提供了前两个示例,而第一个示例提供了更好的性能。这是否可以忽略不计,我不知道。也许进行基准测试并找出答案。

我可以告诉你的是,如果你的结果类型是一个小型结构,你仍然可以保存一些性能点,因为在堆上分配比在堆栈上分配更昂贵。同样,从值类型复制数据可能会超过堆分配惩罚,但如果保持足够小则可能不会。 此外,您可能希望使该类型不可变。

答案 6 :(得分:2)

选项2可能是最佳解决方案,如果它最好地捕获您的域。在这种情况下,您的新类型将在许多其他地方使用,因为它代表一个逻辑实体。

答案 7 :(得分:1)

这完全取决于IntersectResult概念的连贯性。

从技术上讲,没有理由偏爱一个而不是另一个。