例如,沿着:
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;}
}
对于清晰度,易用性以及最重要的性能而言,哪个更好。
答案 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概念的连贯性。
从技术上讲,没有理由偏爱一个而不是另一个。