C#静态方法与实例方法

26
我冒昧地问一个可能很初级的问题。我想给一个类添加一个方法,该类可能在任何时候都有成千上万个实例存在于内存中。现在,另一种选择是创建一个静态类,其中包含一个静态方法,并在该类中创建[method],而不是在类中创建一个实例方法。像这样:

这个:

public static class PetOwner
{
    public static void RenamePet(Pet pet, string newName)
    {
        pet.Name = newName;
    }
}

不要这样做:

public class Pet
{
    public string Name { get; set; }

    public void Rename(string newName)
    {
        this.Name = newName;
    }
}

我想知道静态类的替代方案是否会占用更少的内存。

谢谢!


1
看一下这个问题的一些答案:https://dev59.com/LXVC5IYBdhLWcg3w1E7w。 - adrianbanks
8个回答

33

仅数据字段需要每个实例都存储,受某些条件和优化的影响。例如,定义一个Int32成员的类的10000个实例将消耗约40000字节的内存。

另一方面,由于内部化,字符串并不那么简单。您可以尝试以下示例:

object.ReferenceEquals("", string.Empty) // returns true!
如果这10,000个实例还定义了一个字符串,并且每个实例的字符串都相同,那么你将拥有指向相同数据的10,000个引用,从而大大降低了开销(但在32位环境中,还会占用另外40,000个字节的引用)。
正如其他人所指出的,静态方法和实例方法都只会被加载一次。选择使用实例方法或静态方法并不受性能原因的影响,而是取决于方法的预期使用方式。在您的情况下,该方法需要类型的实例作为其第一个参数,因此它可以是一个实例方法。
根据我的经验,静态方法最常用作返回新实例的类型构造函数的替代方法。构造函数一旦被调用,就必须返回一个新实例或抛出异常,这可能是不可取的。如果出现问题,静态方法可以返回 null,或者返回缓存的实例(如果构造函数是一个昂贵的操作,则后者非常有用)。

1
Joe:并不是所有的字符串都会被内部化,我认为那个例子很糟糕。 - BrokenGlass
我并没有说所有的字符串都是被interned的,而且我认为这是我试图表达的观点的一个很好的例子。 - Quick Joe Smith
1
请注意,只有在编译器的最新版本中,“”和“String.Empty”才是同一实例。在早期版本中,它们是分开的。 - Guffa

18

方法只存在一次内存中,无论它们是否是静态的,因此在内存使用方面没有任何区别。


3
实际上,所有方法调用都像静态调用一样有效。只是这些细节被隐藏在幕后。 - AbstractDissonance
扩展方法是一个很好的例子。唯一的区别是它们是针对另一个类定义的,并且没有访问该实例的受保护/私有成员的权限。 - Quick Joe Smith
通用方法会根据通用签名进行一次即时编译,因此尽管它们存在多次,但只有一次编译。 - Roger Johansson
@RogerAlsing:是的,但在那里,实例方法和静态方法之间也没有区别。 - Guffa

5
除了Guffa的回答之外:
方法只存在于内存中一次,无论它们是静态的还是非静态的,因此在内存使用上没有任何区别。
实例方法将一个类实例作为不可见参数传递给它(可以通过this显式访问),从本质上使得void Rename(string newName)中的newName成为传递给实例方法的第二个参数;因此,static void RenamePet(Pet pet, string newName)void Rename(string newName)的生成指令看起来基本相同,因此它们在性能或其他方面没有任何区别。

2

无论是静态方法还是实例方法,它们只会在加载并经过JIT编译后占用一次内存。它们是实例方法并不会因为此而消耗更多的内存。


1

一个类的实例数量不影响方法所使用的内存。


0

关于内存,静态方法和实例方法是相同的,除了实例方法可以包含数据成员,而静态方法只能有静态数据成员。


0

在内存方面没有任何区别,方法只存在一次内存中,无论是静态方法还是实例方法,但静态方法比实例方法稍微更快地调用,因为实例方法实际上使用“this”实例指针作为第一个参数,因此实例方法总会有这种开销。

因此,在性能方面,静态方法与实例方法相比几乎没有优势,但在内存使用方面,它们都使用相同的数量。


我经常读到这个,但是对我来说没有意义。如果一个实例方法访问实例,那么一个等价的静态方法将具有相同数量的参数。 - Bananach

-2

你根本不需要一个名为“Rename”的方法;你已经有了一个,那就是Name属性的setter。实例方法不需要在每个实例上占用更多的内存,那样做很愚蠢。

在对代码进行性能分析之前,不要做出这样的设计决策。向类中添加一个实例方法不会导致性能瓶颈,即使确实存在性能问题,也不应该基于猜测而开始使用不良设计;先证明它是瓶颈,然后根据需要进行调整。


1
这应该是一条注释,而不是一个答案。 - Shimmy Weitzhandler
@Shimmy:不,这 就是 答案,只是它不是 OP 所期望的。仅因为你只能想到一种解决方案并不意味着不存在更好的选择。方法不需要每个实例内存,所以问题的整个基础是错误的。 - Ed S.

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