【问题标题】:Is a class instantiated when a static method is called in a non-static class?在非静态类中调用静态方法时是否实例化了一个类?
【发布时间】:2011-03-09 06:24:43
【问题描述】:

在 Bar 类中调用 Foo.SomeCheck() 时究竟会发生什么?是否为了调用 SomeCheck() 而创建了 Foo 的实例?如果是,这个实例是否存储在堆上,是否曾经通过垃圾收集器收集?

public class Foo() {
    public static bool SomeCheck() {
        return true;
    }
}

public class Bar() {
    public void SomeMethod() {
        // what happens when we access Foo to call SomeCheck?
        if (Foo.SomeCheck()) {
            //do something
        }
    }
}

【问题讨论】:

  • 如果您不创建 Bar 的实例,您还能如何访问它?
  • 我认为他的意思是“是否需要创建 Foo 的实例”

标签: c# oop class static methods


【解决方案1】:

静态方法与实例方法的不同之处在于,它们所属的类的没有实例需要被创建才能被调用。当您调用静态方法时,实际上是使用类型的名称而不是类型的实例进行调用 - 这应该强化了静态方法不会在实例上调用的想法。 需要重复和强调:不需要类的实例来调用该类的公共静态方法。

现在,您的示例格式错误,但大概行:if( Foo.SomeCheck() ) 正在使用类型名称调用SomeCheck 静态方法:Foo - 不是实例。 但是,必须实例化 Bar 才能进行此调用 - 但是,在您的示例中,您没有格式良好的 Bar 实例。代码通常必须存在于方法(或成员初始值设定项)中——你在这里没有。

回答您问题的其他部分。假设有问题的代码是实例方法的一部分,则必须实例化 Bar - 并调用该方法。 something 必须创建或以其他方式获取Bar 的实例。引用类型将始终在堆上创建 - 但这在很大程度上无关紧要。

至于垃圾回收,您通常不必担心这一点。 .NET 运行时确保清除程序中任何根对象未引用的实例。根通常是位于调用堆栈上某处的实例,或者由一种或另一种类型的静态成员引用。由于我们在这里看不到任何创建或引用Bar 的代码,因此不可能说何时会收集它。例如,如果Bar 是一个单例并存储在某个静态变量中,它可能会存在很长时间——也许是程序的整个生命周期。如果不查看所有操作和管理Bar 的代码,您将无法真正了解。

【讨论】:

  • 很抱歉,我意识到我的原始帖子令人困惑。我知道 Bar 必须被实例化,但是当我们从 Bar 实例中调用 SomeCheck 时,Foo 是否隐式实例化?
  • 一句话,没有。您可以在 Foo 中调用静态方法,而无需实例化任何 Foo 实例。
  • 我认为应该澄清一个类的单个“静态实例”的实例化。通常,这是完全隐含的,CLR 会为您处理它。但是,可以有一个静态构造函数,它在第一次访问类的任何静态成员时执行。静态构造函数在创建静态实例并将其放置在加载器堆上时执行一次,并可用于自定义类的静态实例的静态状态。有关详细信息,请参阅我的答案中的文章。
【解决方案2】:

这取决于SomeMethod的实现。该方法必须从某个地方调用,大概是一个“驱动程序”类,它将实例化 Bar 并调用SomeMethod。例如:

public class Driver
{
    public static void Main()
    {
        Bar bar = new Bar();
        bar.SomeMethod();
    }
}

鉴于您当前的 SomeMethod 实现,是的,您必须实例化它。

但是,只要SomeMethod 只调用另一个静态方法,我们也可以将 it 设为静态。在这种情况下,您不必创建 Bar 的实例来调用该方法。即

public class Driver
{
    public static void Main()
    {
        Bar.SomeMethod();
    }
}

【讨论】:

    【解决方案3】:
    public class Manipulate
        {        
            public static int Main(string[] args) {
                Bar bar = new Bar();
                bar.BarFoo();
                Console.ReadKey();
                return 0;
            }
    
        }
        public class Foo {
            public static bool SomeCheck() {
                return true;
            }
        }
        public class Bar {
            // what happens when we access Foo to call SomeCheck?
            public void BarFoo() {
                if (Foo.SomeCheck()) {
                    Console.WriteLine("Hello am true");
                }
            }
        }
    

    是的,您需要创建 Bar 的实例,但不需要为 Foo 类创建实例,因为它是静态方法。唯一不同的是,静态方法是在类级别(编译时)而不是对象级别(运行时)调用的,因此您不需要实例化 Foo 类。

    【讨论】:

      【解决方案4】:

      我强烈推荐阅读以下文章:

      Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects

      它解释了 .NET 运行时如何在低级别工作,并解释了加载器堆等内部细微差别以及静态类/成员的工作方式。从技术上讲,存在类静态成员的“静态实例”的初始实例化。但是,运行时处理此启动的方式与处理类实例的方式不同。静态类存储在加载器堆上,它们不是 GC 管理的。加载程序堆以静态方式分配和增长,而不是压缩。这篇文章非常适合阅读,应该能让您彻底了解 CLR 的运作方式。

      (注意:我不确定这篇文章对于 .NET 4 的有效性。我确实知道 .NET 4 中有 GC 更改,但是我不确定有多少基本的运行时更改。介绍DLR 和其他特性可能与上述文章中的解释有一定的偏差。)

      【讨论】:

        【解决方案5】:

        Foo 不需要实例化,也不会在SomeCheck 静态方法调用时根据结果实例化,您将获得方法本身返回的值,而不是类的实例。

        请查看这些参考资料以了解更多详情:

        1. Static vs Non-Static Methods;
        2. Static Classes and Static Class Members (C# Programming Guide)。

        我希望这会有所帮助! =)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-23
          • 1970-01-01
          • 2015-02-13
          • 1970-01-01
          • 1970-01-01
          • 2016-06-26
          • 2011-01-17
          相关资源
          最近更新 更多