14

例如,沿着:

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

}

对比

public IntersectResult Intersect (Ray ray)
{

}

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

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

4

8 回答 8

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;
    }
} 
于 2011-03-10T22:14:54.267 回答
10

我会使用组合类型。

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

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

于 2011-03-10T21:22:43.767 回答
6

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

于 2011-03-10T21:20:58.983 回答
5

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

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

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

于 2011-03-10T21:22:48.427 回答
3

您可以使用 Tuple 而不是创建 IntersectResult 类作为替代方案。

参考:

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

但是,我肯定会倾向于返回复杂类型而不是输出参数。

于 2011-03-10T21:21:31.637 回答
3

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

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

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

于 2011-03-10T21:38:19.870 回答
2

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

于 2011-03-10T21:23:43.583 回答
1

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

从技术上讲,没有理由偏爱其中一个。

于 2011-03-10T21:22:19.863 回答