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

C# 需要值时静态与实例方法的最佳实践,c#,static-methods,instance-methods,C#,Static Methods,Instance Methods,我在这里读过关于静态方法和实例方法的文章,但我看不到任何能回答这个特定问题的方法(尽管可能是绿色的) 当您有一个具有某些属性的类,并且该类中的方法需要使用这些属性时,使用静态方法还是实例方法更好 即 sumbar1和bar2方法需要同时存在Foo类的属性。创建一个静态方法并以这种方式调用它似乎有点愚蠢,因为我正在手动将类的成员传递到类的方法中: Foo foo1 = new Foo(); foo1.bar1 = x; foo1.bar2 = y; sumbar1andbar2(foo1.bar1

我在这里读过关于静态方法和实例方法的文章,但我看不到任何能回答这个特定问题的方法(尽管可能是绿色的)

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

sumbar1和bar2方法需要同时存在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,则该方法看起来更好。

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

如果它是一个通用计算(如您的示例),您可能希望在其他地方使用它,那么您有几个选项:

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

public static class MyUtility {
    public static Int32 Add(Int32 x, Int32 y) { return x + y; }
}
让它成为一个基于Foo的父类,或者定义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; }
}

首先,有一种简单易行的方法来确保属性不为null:这称为封装。确保在构造函数中设置了属性,如果选择公开属性,则在其setter中进行验证。这样,在构造对象后,属性将不为null(否则,构造函数将引发异常),而属性设置器将使属性值保持一致状态(否则,设置器将引发异常)

现在进入实际问题:如果一个或两个值可能来自
Foo
的其他实例,请将计算方法设置为静态。否则,将其作为实例方法


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

如果它涉及特定实例,那么它必须是实例成员(无论是方法、属性还是字段)。这些是最常见的情况,因此例子很多

如果它与特定实例无关,则实例成员需要一个实例,而您不会以任何其他方式使用该实例。一个很好的例子是
Math.Max
,如果调用
Math.Max(43,23)
,那么结果与43大于23这一事实有关,而不是与
Math
对象的任何属性有关,这些属性在应用程序的运行过程中可能会发生变化

有些类只需要静态成员,所以我们将类本身设置为静态的,它根本无法实例化

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

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

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

或:

这种特殊情况常用(
TimeSpan.Zero
)和/或不便于记忆(
int.MaxValue
2147483647
甚至
0x7FFFFFFF
更清晰、更容易回忆)。当然,如果两者都是,那就更是如此

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

  • 您无权访问该类的源(它是另一方的类)
  • 您希望在接口而不是类上定义它
  • 您希望它可以在null上调用(避免,这在C中是非惯用的,尽管在其他语言中更常见)
  • 您希望为泛型的特定情况定义它。例如,如果我创建了实现了
    IDictionary
    MyDictionary
    ,我就不能定义一个向存储值中添加数字的
    plus
    方法,因为只有当TValue是已知的数字类型时,该方法才能工作。我可以将这种方法定义为一种扩展方法,如
    int Plus(这个MyDictionary dict,int addend)
    ,当
    TValue
    为int时,它会像实例成员一样出现,但不会干扰对其他类型参数使用
    MyDictionary

  • 所有这些情况都让您别无选择,只能使用扩展方法,但不要在实例成员执行任务时使用它们。更清楚的是,特别是其他一些.NET语言只会将扩展成员视为静态成员。

    发布实际编译的代码,以便我们可以尝试一个可能的答案。没有理由为您拥有的类创建扩展方法。我同意。我没说他应该,谢谢dasblinkenlight!这就是我要找的。封装是我一直在寻找的。由于自学成才,我错过了很多人在101课程中可能学到的一些东西。
    // 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; }
    }