C#中的静态方法和实例方法

6

我正在编写一个应用程序,希望它具有极高的可扩展性,而扩展方法似乎给了我想要的,还可以在没有实例的情况下调用它们,这也是我需要的。

我记得读过静态方法比实例方法更快,但不会获得GC的优势。这是正确的吗?

除非我找到一种更好的设计替代品,否则我很可能不会改变我的设计。但是,为了获取额外的信息,我仍然想知道速度、GC等方面的差异。

编辑:谢谢。更多信息:假设我们有一个Person类:

class Person

可以有一个实例距离方法,例如:

this.Distance (Person p)

这很好,但这并不能让我在不创建Person类的实例的情况下计算两个点(比如Point3)之间的距离。
我的需求是这样的:
class Person (no Distance methods)

但是 Distance 的扩展方法:

Distance (this Person, Person)
Distance (this Point3, Point3)

这样我就可以同时完成以下两个任务:

myPerson.Distance (yourPerson)

并且

Extensions.Distance (pointA, pointB)

编辑2:@Jon,是的,我认为这就是(不理解GC的优势)的意思,但我不知道静态方法会产生这种负担/开销。
4个回答

14

选择静态方法和实例方法是面向对象设计的问题。如果你正在编写的方法是一个对象的行为,那么它应该是一个实例方法。如果它不依赖于对象的实例,那么它应该是静态方法。

基本上,静态方法属于类型,而实例方法属于类型的实例。


这是否意味着对于一个客户对象,拥有像“GetAge()”这样的实例方法和像“CustomerHelper.GetCustomersFromState(int stateId)”这样的静态方法是正确的做法? - Shyju
GetAge 作为实例方法是有意义的。但后者我不太清楚。 - Mehrdad Afshari
@MehrdadAfshari,所以CustomerDAL类有一个GetAllCustomers(..)方法....与EmployeeDALGetAllEmployees()相同.....应该是实例方法吗?如果是这样-那么在这些类中应该有哪种类型的静态方法(请提供示例)? - Royi Namir

13
你所说的“不使用GC的优势”是什么意思?方法并非垃圾收集,实例才是。
虚方法比非虚方法略慢,我猜在任何实例方法之前都会有繁琐的空值检查,但这并不重要。选择最合适的设计方案。
然而,静态方法在测试时非常麻烦-例如,如果您在调用某些静态方法来验证方法Foo()中的身份验证,则在测试 Foo()时无法使其仅调用模拟验证器(除非静态方法本身允许您执行该操作)。但是,如果您为要测试的任何内容的原始实例提供了某个接口的模拟实现,其中包含一个Authenticate() 方法,则可以使它按您想要的方式运行。
编辑:在这种情况下,听起来您真正需要的是在Point类型上的实例方法,以计算两个点之间的距离(“此”点和另一个点之间的距离)-或者可能是 Distance 类型上的静态工厂方法。

谢谢Jon。现在有意义了吗? - Joan Venge
谢谢Jon。我没有距离类型,因为它只是一个浮点数。实例的问题是我不确定是否要使用。比如我可以在Point中有Distance方法,但我也想有PointToRGBColor,但在我的设计中,这个RGB方法似乎也应该是扩展方法。 - Joan Venge
现在在这里实例化完全有意义,比如 RGB 方法可以在 Color 类中。但是如果必须有一个实例才能使用功能,则会阻止我直接使用功能。对于 Point 来说,这没问题,因为它是最基本的类型之一,需要拥有它来使用它。 - Joan Venge
嗯...我不是很理解Point/RGB的例子。如果它只适用于某些情况(而不是任何点,在任何用法中都适用),那么将其作为扩展方法可能是有意义的。我建议不要过度使用扩展方法。 - Jon Skeet

5
如果您所说的“快”是指方法内部代码的执行速度更快,那么答案是否定的。静态方法中的代码和非静态方法中的代码一样快。
如果您在谈论调用方法的开销,那就有点复杂了。对于像Java这样的语言,实例调用的开销更大是很常见的。但是在C#中并非如此,因为默认情况下实例方法不是虚拟的。
因此,虚拟方法的开销略高于非虚拟方法。静态方法无法是虚拟的,但是实例方法可以声明为虚拟的。
至于垃圾收集,这主要与字段有关而不是方法。静态字段可以从代码的任何地方访问,因此垃圾收集器无法确定引用是否会再次使用,因此它永远不会被回收。

2

根据您的示例,我会编写一个静态实用函数来查找两点之间的距离:

public static class Geometry
{
    public static double GetDistanceBetween(Point a, Point b) { ... }
}

接下来,我会给Person一个名为Position的属性,它返回一个点。这样我就可以写:

double distance = Geometry.GetDistanceBetween(personA.Position, personB.Position);

这段话与英语几乎一样,为什么要让它更加晦涩难懂呢?如果你将 Distance 变成一个方法,那么你就可以这样写:

personA.Distance(personB)

或者:

personB.Distance(personA)

这两种顺序没有区别,但使用方法调用语法可能会暗示存在差异。


谢谢,是的,“Position”是我“Person”类中的一个属性。但是这些操作是由用户通过原理图构建的。因此,我希望有一种统一的方法来完成它,但也可以直接使用它们而不必在UI中创建任何原理图项。 - Joan Venge
顺便说一下,我没有理解第二部分。你的意思是personA.Distance(personB)和Extensions.Distance是一样的吗? - Joan Venge
因此,该图可以包含无限数量的连接。而这个“人物,距离”只是一个例子。但是假设您有“加”,“减”等许多许多选项。因此,实际上该图也不是线性的。它是一棵树,我必须更新它以显示最终结果。 - Joan Venge
对的,它几乎像一个需要被评估的图形表达式?鉴于这种灵活性,我无法理解为什么您会像在“LINQ风格”示例中那样硬编码调用链。相反,您的代码将循环遍历列表并要求对象评估事物。 - Daniel Earwicker
@DanielEarwicker,我想你在写“如果你将Distance变成一个方法”的时候,应该是指“实例方法”,对吗?因此,personA.Distance(person)将作用于某个本地变量personA.Possition。你可能希望在这里口头明确一下。 - Zack Jannsen
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接