需要值时静态与实例方法的最佳实践

时间:2012-01-11 23:41:03

标签: c# static-methods instance-methods

我在这里读过关于静态与实例方法的内容,但我没有看到任何回答这个特定问题的答案(绿色可能)。

当你有一个具有某些属性的类,以及该类中需要使用这些属性的方法时,使用静态方法还是实例方法更好?

class Foo
{
    //properties
    bar1;
    bar2;

    //method
    sumbar1andbar2()
    {
        return bar1 + bar2;
    }
}

sumbar1andbar2方法需要存在Foo类的两个属性。制作静态方法并以这种方式调用它似乎有点傻,因为我手动将类的成员传递给类的方法:

Foo foo1 = new Foo();
foo1.bar1 = x;
foo1.bar2 = y;
sumbar1andbar2(foo1.bar1, foo1.bar2); 

但是虽然下面的实例方法看起来更清晰,但我不知道一种干净简单的方法来确保bar1和bar2都不为null,这会导致异常:

Foo foo1 = new Foo();
foo1.bar1 = x;
foo1.bar2 = y;
sumbar1andbar2();

但是,如果方法修改了类的另一个属性,例如bar3,则实例方法似乎更好。

3 个答案:

答案 0 :(得分:2)

如果方法的行为对于Foo类型是唯一的(并且没有在其他地方有意义地应用),或者如果它修改了Foo的状态,那么你应该把它作为Foo的实例方法。

如果它是一个通用的计算(如你的例子),你可能想在别处使用它,你有几个选择:

使其成为实用程序类的静态方法,例如

public static class MyUtility {
    public static Int32 Add(Int32 x, Int32 y) { return x + y; }
}

在Foo上创建extension method,它是父类,或者是定义x和y的接口,例如。

// Use as follows:
// var f = new Foo() { x = 5, y = 5 };
// var ten = f.MyUtility();
public static class MyUtility {
    public static Int32 Add(this Foo foo) { return Foo.x + Foo.y; }
}

答案 1 :(得分:2)

如果它与特定实例有关,那么它必须是实例成员(无论是方法,属性还是字段)。这些是最常见的情况,所以例子很丰富。

如果它与特定实例无关,那么实例成员需要一个您不会以任何其他方式使用的实例。一个很好的例子是Math.Max,如果你调用Math.Max(43, 23),那么结果与43大于23的事实相关,而不是Math对象的任何属性,可以想象地改变应用程序运行的过程。

有些类只需要静态成员,因此我们将类本身设为静态,并且根本无法实例化。

由于同样的原因,与类的性质而不是给定实例相关的属性也应该是静态的。例如。 int.MaxValueint的属性,而非int.MaxValue。 93。

请注意,int的结果本身就是TimeSpan.Zero。这并不罕见。其他示例包括string.Emptyint。这可以是一种方便,有时也可以防止大量重复引用类型的性能优势(在值类型的情况下不相关,在引用类型的情况下不会过度说明)。重要的是不要过度这样做。我们不希望在TimeSpan.Zero上使用4294967296不同的静态属性来让它们“轻松”调用它们!通常,这在以下情况下很有用:

特殊情况不能由构造函数构造。

OR:

特殊情况常用(int.MaxValue)和/或不方便记住(21474836470x7FFFFFFF甚至MyDictionary<TKey, TValue>更清晰,更易于回忆。如果两者都是如此,那就更是如此。

扩展方法是静态方法,可以像实例成员一样调用它们。它们非常方便,但通常情况下最好使用实例成员。当实例成员不可能时,它们很有用,因为:

  1. 您无法访问课程的来源(这是另一方的课程)。
  2. 您希望在界面而不是类上定义它。
  3. 您希望它在null上可调用(避免,这在C#中是非惯用的,但在其他语言中更常见)。
  4. 您想为特定的泛型案例定义它。例如,如果我创建了实现IDictionary<TKey, TValue>的{​​{1}}我无法定义一个plus方法,该方法会将一个数字添加到存储值,因为这只能在TValue是已知数字类型时才能使用。我可以将这样的方法定义为类似int Plus<TKey>(this MyDictionary<TKey, int> dict, int addend)的扩展方法,当TValue为int时,它将显示为实例成员,但不会干扰MyDictionary对其他类型的使用参数。
  5. 所有这些情况都让您别无选择,只能使用扩展方法,但在实例成员执行此任务时不要使用它们。它更清楚,特别是因为其他一些.NET语言只会将扩展成员看作静态。

答案 2 :(得分:1)

首先,有一种很简单的方法可以确保您的属性不为null:它被称为封装。确保在构造函数中设置属性,如果选择公开属性,则在其setter中进行验证。这样,在构造对象之后属性将为非null(否则,构造函数将抛出异常),并且属性setter将使属性值保持一致状态(否则,setter将抛出异常)。

现在回答实际问题:如果其中一个或两个值可能来自Foo的其他实例,请使计算方法保持静态。否则,将其设为实例方法。

P.S。如果您的方法返回一个值,没有参数,并且不产生副作用,请考虑将其设置为计算属性而不是方法。