将派生值公开为属性是否是好的形式?

时间:2009-07-17 01:06:11

标签: c# .net idioms

我需要在给出7个潜在输入的情况下得出一个重要的值。 Uncle Bob敦促我避免使用那么多参数的函数,所以我extracted the class。所有参数现在都是属性,我留下了一个没有参数的计算方法。

“那个”,我认为,“可能是一个属性,但我不确定这是否是惯用的C#。”

我应该将最终结果公开为属性,还是作为没有参数的方法?一般的C#程序员会发现令人困惑或令人反感的属性吗?那么Alt.Net的人群呢?

decimal consumption = calculator.GetConsumption(); // obviously derived
decimal consumption = calculator.Consumption; // not so obvious

如果是后者:我应该将中间结果声明为[私有]属性吗?感谢沉重的method extraction,我有几个中期结果。其中许多不应该是公共API的一部分。但是,其中一些可能很有趣,如果我可以将它们作为属性访问,我的表达式会更清晰:

decimal interim2 = this.ImportantInterimValue * otherval;

快乐实验部门:

在VS2008中调试我的代码时,我注意到我一直将鼠标悬停在计算中间结果的方法调用上,期望将鼠标悬停在返回值上。在将所有方法转换为属性之后,我发现暴露中间结果作为属性极大地帮助了调试。我对此很满意,但对可读性持续存在担忧。

临时价值声明看起来更加混乱。但是,没有括号,表达式更容易阅读。我不再觉得有必要用动词启动方法名称。相比之下:

// Clean method declaration; compulsive verby name; callers need
// parenthesis despite lack of any arguments.
decimal DetermineImportantInterimValue() {
    return this.DetermineOtherInterimValue() * this.SomeProperty;
}

// Messier property declaration; clean name; clean access syntax
decimal ImportantInterimValue {
    get {
        return this.OtherInterimValue * this.SomeProperty;
    }
}

我或许应该解释一下,我已经用Python编写了十年。我一直倾向于花费额外的时间让我的代码更容易调用而不是写。我不确定Python社区是否会将这种面向属性的风格视为可接受的“Pythonic”,但是:

def determineImportantInterimValue(self):
    "The usual way of doing it."
    return self.determineOtherInterimValue() * self.someAttribute

importantInterimValue = property(
    lambda self => self.otherInterimValue * self.someAttribute, 
    doc = "I'm not sure if this is Pythonic...")

5 个答案:

答案 0 :(得分:5)

这里的重要问题似乎是:

从长远来看,哪一个为您生成更易读,可维护的代码?

在我个人看来,将单个计算隔离为属性与单个单一哥特式方法调用相比具有几个明显的优势:

  • 无论您使用何种类方法,都可以在调试器中看到计算结果。这是您在调试时提高工作效率的好处。

  • 如果计算是离散的,属性将很快执行,这意味着(在我看来),他们遵守财产设计的规则。认为设计指南应该被视为一件紧身衣是荒谬的。记住:没有银弹。

  • 如果计算被标记为私有或内部,则不会给班级的消费者增加不必要的复杂性。

  • 如果所有属性都足够离散,编译器内联可能会解决您的性能问题。

  • 最后,如果返回最终计算的最终方法很容易维护和理解,因为你可以阅读它,这本身就是一个非常引人注目的论据。

你能做的最好的事情之一就是自己思考,敢于挑战我们的同行和前辈的先入为主的理念。每条规则都有例外。这种情况很可能就是其中之一。

<强>后记: 在绝大多数情况下,我不认为我们应该放弃标准的财产设计。但有些情况下需要偏离The Standard(TM),因为这样做是有意义的。

答案 1 :(得分:3)

就个人而言,如果您将公共API作为方法而不是属性,我更愿意。在C#中,属性应该尽可能“快”。有关此讨论的更多详细信息:Properties vs Methods

在内部,GetConsumption可以使用任意数量的私有属性来获得结果,选择是你的。

答案 2 :(得分:3)

我通常会按照方法或属性的方式去做。如果它需要一些时间,我将使用一种方法。如果它很快或在幕后进行的操作非常少,我会把它变成一个属性。

答案 3 :(得分:0)

我使用方法来表示对象的任何操作或更改对象的状态。所以,在这种情况下,我会将函数命名为CalculateConsumption(),它计算其他属性的值。

答案 4 :(得分:0)

您说您从七个输入中获取值,您已经实现了七个属性,每个输入一个属性,并且您有一个属性getter用于结果。您可能需要考虑的一些事项是:

  • 如果调用者未能设置七个“输入”属性中的一个或多个,会发生什么?结果仍然有意义吗?是否会抛出异常(例如除以零)?

  • 在某些情况下,API可能不太容易被发现。如果我必须调用一个带七个参数的方法,我知道我必须提供所有七个参数才能得到结果。如果某些参数是可选的,则该方法的不同重载会清楚哪些参数。

    相比之下,我可能不太清楚我必须在访问“结果”属性之前设置七个属性,并且很容易忘记一个属性。

  • 当您拥有包含多个参数的方法时,您可以更轻松地进行更丰富的验证。例如,如果“参数A和参数B都为空”,则可能抛出ArgumentException。

    如果对输入使用属性,则每个属性都将独立设置,因此在设置输入时无法执行验证 - 仅在取消引用结果属性时,这可能不太直观。

相关问题