我需要解释一下为什么我不使用静态方法/属性。例如,
String s=String.Empty;
这个属性(属于.Net框架)错了吗?应该是什么样的?
String s= new EmptySting();
或
IEmptyStringFactory factory=new EmptyStringFactory();
String s= factory.Create();
答案 0 :(得分:9)
为什么每次要使用空字符串时都要创建新对象?基本上空字符串是单个对象。
正如威尔所说,静态 在测试方面确实存在问题,但这并不意味着你应该在所有地方使用静态 。
(我个人更喜欢使用“”代替string.Empty
,但这是在其他地方已经做过的死亡讨论。)
答案 1 :(得分:7)
我认为使用静态的最糟糕的事情是你最终可以在类之间进行紧密耦合。在System.Web.Abstractions出来之前看到ASP.NET。这使得您的类更难以测试,并且可能更容易出现导致系统问题的错误。
答案 2 :(得分:2)
您的三个不同示例的语义非常不同。我会尝试在实践中将其分解。
String s=String.Empty;
这是一个单身人士。当你想要确保只有一件东西时,你会使用它。在这种情况下,由于字符串是不可变的,因此只需要一个“空”字符串。不要过度使用单身人士,因为他们很难测试。但是,当它们有意义时,它们就非常强大。
String s= new EmptySting();
这是您的标准构造函数。您应该尽可能使用它。仅当单身人士的案件势不可挡时才重构单身人士模式。在string.Empty
的情况下,使用单例是非常有意义的,因为不能通过引用类来更改字符串的状态。
IEmptyStringFactory factory=new EmptyStringFactory();
String s= factory.Create();
应该谨慎使用实例工厂和静态工厂,如单身人士。大多数情况下,当类的构造很复杂并且依赖于多个步骤并且可能是状态时,应该使用它们。
如果对象的构造依赖于调用者可能不知道的状态,那么您应该使用实例工厂(如示例中所示)。当构造复杂,但调用者知道会影响构造的条件时,则应使用静态工厂(例如StringFactory.CreateEmpty()
或StringFactory.Create("foo")
。但是,对于字符串,则构造很简单,使用工厂会闻到解决问题的解决方案。
答案 3 :(得分:2)
嗯,在String.Empty
的情况下,它更像是一个常量(类似于Math.PI
或Math.E
)并且是为该类型定义的。为一个特定值创建子类通常是不好的。
关于他们如何“不方便”的另一个(主要)问题:
我发现静态属性和方法在被滥用来创建更具功能性的解决方案时不方便,而不是C#意味着面向对象的方法。
我的大多数静态成员都是上面的常量或类似工厂的方法(如Int.TryParse
)。
如果类有很多静态属性或方法用于定义类所代表的“对象”,我会说这通常是糟糕的设计。
使用静态方法/属性困扰我的一个主要问题是,有时候它们过于束缚于某种做某种事情的方式而没有提供一种简单的方法来创建一个实例,而这些实例提供了对行为的轻松覆盖。例如,假设您想要以度数而不是弧度进行数学计算。由于Math
都是静态的,因此您不能这样做,而是必须每次转换。如果Math
是基于实例的,您可以创建一个新的Math
对象,默认为弧度或度数,并且仍然可以为典型行为提供静态属性。
例如,我希望我能这样说:
Math mD = new Math(AngleMode.Degrees); // ooooh, use one with degrees instead
double x = mD.Sin(angleInDegrees);
但我必须写下这个:
double x = Math.Sin(angleInDegrees * Math.PI / 180);
(当然,你可以为转换编写扩展方法和常量,但是你明白我的意思。)
这可能不是最好的例子,但我希望它传达的问题是无法使用默认变体的方法。它创建了一个功能构造,并打破了通常的面向对象方法。
(作为旁注,在这个例子中,我会为每个模式都有一个静态属性。在我看来,这将是对静态属性的合理使用。)
答案 4 :(得分:1)
通常,创建一个新的空字符串是一个坏主意 - 这会在堆上创建额外的对象,因此垃圾收集器需要额外的工作。当你想要空字符串时,你应该总是使用String.Empty或“”,因为它们是对现有对象的引用。
答案 5 :(得分:1)
通常,静态的目的是确保程序中只有一个静态“事物”实例。
当你知道你正在创建的“东西”在整个程序的生命周期中永远不会改变时,静态很有用。在您的示例中,System.String
定义了一个私有静态字段来存储空字符串,该字符串只分配一次,并通过静态属性公开。
如前所述,静态问题存在可测试性问题。例如,很难模拟静态类型,因为它们无法实例化或派生自。由于它们使用的字段也必须是静态的,因此很难将mocks引入某些静态方法。 (您可以使用静态setter属性来解决此问题,但我个人试图避免这种情况,因为它通常会破坏封装)。
在大多数情况下,使用静力学是o.k.您需要根据程序的复杂性决定何时权衡使用静态实例和实例实体。
答案 6 :(得分:1)
在纯粹的OO方法中,静态方法打破了OO范例,因为您将实际数据附加到数据定义。类是符合语义的一组对象的定义。就像有包含一个或零个元素的数学集一样,可以有只包含一个或零个可能状态的类。
共享公共对象并允许多个actor处于其状态的方式是传递引用。
静态方法的主要问题来自于,如果将来你想要其中两个怎么办?我们正在编写计算机程序,我们可以假设,如果我们能够做出某些东西,我们应该能够非常简单地制作两个,而静态则不是这样。将某些东西从静态状态更改为正常实例状态是完全重写所讨论的类。
我可能会假设我只想使用一个SqlConnection池,但现在如果我想要一个高优先级池和一个低优先级池。如果连接池是实例化的而不是静态的,那么解决方案就很简单了,相反,我必须将池与连接实例化结合起来。我更希望图书馆作家有远见,否则我必须重新实现合并。
编辑: 单继承语言中的静态方法是提供代码重用的一种方法。通常,如果有方法想要在类之间共享公共代码,则可以通过多重继承或mixin将其拉入。单继承语言强制您调用静态方法;没有办法使用带状态的多个抽象类。
答案 7 :(得分:1)
有使用静态的缺点,例如:
使用静态方法的原因
答案 8 :(得分:0)
从场景驱动的设计中,选择静态与实例方法的标准应该是:如果可以在没有要创建的类的实例的情况下调用方法,则将其设置为静态。否则,使它成为一个实例方法。第一个选项使调用成为一次行处理,并避免使用.ctor调用。
此处另一个有用的标准是责任是否在正确的位置。对于前者你有一个帐户类。假设您需要货币转换功能,例如从美元到欧元。你是否成为Account类的成员? account.ConvertTo(Currency.Euro)?或者您是否创建了一个封装该职责的不同类? CurrencyConverter.Convert(account,Currency.Euro)?对我而言,后者在将责任包含在不同类别的意义上更好,而在前者中我会在不同的账户中传播货币转换知识。