2

给定 7 个潜在输入,我需要得出一个重要值Bob 叔叔敦促我避免使用具有这么多参数的函数,所以我提取了 class。现在所有参数都是属性,我只剩下一个没有参数的计算方法。

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

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

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

如果是后者:我也应该将中间结果声明为 [private] properties吗?由于大量的方法提取,我有几个中期结果。其中许多不应该是公共 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...")
4

5 回答 5

5

这里的重要问题似乎是:

从长远来看,哪一个会为您生成更清晰、可维护的代码?

在我个人看来,将单个计算隔离为属性比单个整体方法调用具有几个明显的优势:

  • 无论您使用哪种类方法,您都可以在调试器中看到计算结果。这对您在调试时的工作效率大有裨益。

  • 如果计算是离散的,则属性将执行得非常快,这意味着(在我看来)它们遵守属性设计的规则。认为设计指南应该被视为一件紧身衣是荒谬的。记住:没有灵丹妙药。

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

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

  • 最后,如果返回最终计算的最终方法由于您可以阅读而更容易维护和理解,那么这本身就是一个完全令人信服的论点。

您能做的最好的事情之一就是为自己思考,并敢于挑战我们同行和前辈先入为主的“一刀切”的观念。每条规则都有例外。这个案例很可能就是其中之一。

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

于 2009-07-17T02:51:20.647 回答
3

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

在内部,GetConsumption 可以使用任意数量的私有属性来得出结果,选择权在您手中。

于 2009-07-17T01:10:29.147 回答
3

我通常会按照方法或属性的作用。如果需要一点时间,我会使用一种方法。如果它非常快或在幕后进行的操作非常少,我会将其设为属性。

于 2009-07-17T01:12:24.687 回答
0

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

于 2009-07-17T03:00:01.817 回答
0

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

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

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

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

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

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

于 2009-07-17T06:30:54.853 回答