【问题标题】:private static and private methods dilemma私有静态和私有方法的困境
【发布时间】:2020-03-30 18:49:20
【问题描述】:

嗨(再次提出哲学问题),

有一个 private static 方法无法访问实例字段,就我的阅读而言,这些方法提供了一点性能改进。

将方法标记为静态后,编译器将发出 这些成员的非虚拟呼叫站点。发出非虚拟呼叫 站点将阻止在运行时对每个调用进行检查,以确保 当前对象指针非空。这可能会导致 性能敏感代码的可衡量的性能增益。在一些 情况下,访问当前对象实例失败代表一个 正确性问题。

FxCop

那么既然我们可以在private static方法参数中放置一个实例字段,为什么还要使用普通的private方法呢?这不是效率低吗?

只是为了形象化我的意思:

public class A
{
    private readonly string _key = "ssss";

    public bool IsGoodKey()
    {
    }

    private static bool ValidateKeyStatic(string key)
    {
        return string.IsNullOrEmpty(key);
    }
    private bool ValidateKey()
    {
        return string.IsNullOrEmpty(_key);
    }
}

现在有两种实现IsGoodKey的方法:

    public bool IsGoodKey()
    {
        return ValidateKeyStatic(_key);
    }

还有

    public bool IsGoodKey()
    {
        return ValidateKey();
    }

为什么不总是使用private static

【问题讨论】:

  • 您为改变您的状态所做的任何操作都必须是公开的,并且不会采用与参数相同的类型。通常我们将类中的辅助方法用作私有静态或公共静态,以便为了获取新对象或进行某些操作,我不需要在不知道其依赖关系的情况下新建整个对象。希望这能澄清你的问题。
  • @Saravanan:“你为改变你的状态所做的任何操作都必须是公开的”这根本不是真的。同样“并且不会采用与参数相同的类型” - 嗯,它可能。例如,您可以有一种方法将属性从一个对象复制到另一个对象。
  • @JonSkeet,你说得对,我想说它应该是非静态的,但应该是公开的

标签: c#


【解决方案1】:

如果性能是static 的全部目的,那么您当然可以任何事情 static。但实际上关键字不是关于 performance,而是关于 semantics - 也就是说,该成员是否属于 class(因此所有实例),还是到您的类的特定实例?

想象一下,您将创建类的多个实例。如果每个实例都有一个_key,那么您肯定需要一个instance 字段,而不是static 字段。另一方面,如果所有实例共享相同的密钥,您当然可以设为static

毕竟性能只是关键字的副作用,您不应该永远根据纯粹的性能考虑做出设计决策。

【讨论】:

  • 为什么呼叫不是虚拟的? ValidateKey() 是一个虚拟调用,该方法是否被定义为虚拟的事实并没有什么区别。发出的 IL 仍然是 callvirt,当调用该方法时,编译器将始终必须对 this 执行非空检查。
  • 你说的是完全正确的,但我觉得我的问题还没有在这里得到回答。静态方法的行为被封装在类中。假设_key 在其他情况下具有不同的值。为什么不使用具有合适参数的“静态”方法来用该方法得出结论。 OOP 仍然不会被验证,并且只需添加static(这只是一个词),代码就会更高效。我可以理解这里的一个担忧。 private static 不应更改类状态。但在其他情况下,我看不到任何缺点
  • @Pawel 好吧,让每个方法都是静态的,你完全失去了 OOP 的任何好处,即多态性。您将如何覆盖基类中的任何方法?您将如何实现任何接口?
  • @HimBromBeere 是的,我不是在谈论公共方法。这里只有私人的:)
  • @Pawel 好的,我明白你的意思了。所以不要使用myInstance.DoSomething(),而是使用DoSomething(myInstance),对吗?好吧,我想性能优势对于大多数场景来说是可以忽略不计的,而我上面提到的实际语义更为重要。
【解决方案2】:

将方法标记为静态后,编译器将向这些成员发出非虚拟调用站点。发出非虚拟调用站点将阻止在运行时对每个调用进行检查,以确保当前对象指针不为空。这可以为性能敏感的代码带来可衡量的性能提升。在某些情况下,无法访问当前对象实例表示正确性问题。

我可能是错的,但我认为您混淆了“当前对象指针非空”检查和静态调用 ValidateKeyStatic(_key); 中传递给静态方法 _key 的参数检查

第一个是指调用方法的指针。当您编写以下对非静态方法的调用时:

foo.Bar(blah)

foo 插入一个自动非空运行时检查,如果它恰好是null,你会得到臭名昭著的ReferenceNullException,你的程序就会崩溃。如果方法是静态的,则可以避免此检查,从而提高性能,因为不会在对象的任何特定实例上调用该方法。

这与您可能对blah 执行的检查无关,而是使用Bar 方法。

【讨论】:

    猜你喜欢
    • 2017-05-23
    • 2012-07-14
    • 2022-11-20
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 2015-12-29
    相关资源
    最近更新 更多