C#中静态变量有什么用途?什么时候使用它?为什么不能在方法内声明静态变量?

120

我已经搜索了关于C#中静态变量的信息,但仍然不理解它的用途。另外,如果我尝试在方法内声明该变量,它将不允许我这样做。为什么呢?

我看过一些关于静态变量的例子。我知道我们不需要创建类的实例来访问该变量,但这还不足以理解其用途和何时使用它。

第二件事

class Book
{
    public static int myInt = 0;
}

public class Exercise
{
    static void Main()
    {
        Book book = new Book();

        Console.WriteLine(book.myInt); // Shows error. Why does it show me error?
                                       // Can't I access the static variable 
                                       // by making the instance of a class?

        Console.ReadKey();
    }
}

4
你是不是指的是“静态字段”? - Dennis
就像我们在类中声明静态整数i=5一样。 - Kartik Patel
3
VB.NET支持本地静态变量。他们必须实现它以保持与vb的兼容性。它生成的代码量巨大,局部静态变量很难处理,因为它们不是线程安全的。字段也不是线程安全的,但是每个人都期望如此。 - Hans Passant
如果您得到了想要的信息,请不要忘记将答案标记为已接受... - Pranay Rana
4
你可以通过类型(在这种情况下为 Book)访问静态变量/方法,而不是通过实例(book),因此更简单的解决方案是 Book.myInt - Jaider
12个回答

191

static变量将其值在类的所有实例之间共享。

没有声明为static的示例:

public class Variable
{
    public int i = 5;
    public void test()
    {
        i = i + 5;
        Console.WriteLine(i);
    }
}


public class Exercise
{
    static void Main()
    {
        Variable var1 = new Variable();
        var1.test();
        Variable var2 = new Variable();
        var2.test();
        Console.ReadKey();
    }
}

解释:如果你看上面的例子,我只是声明了一个int变量。当我运行这段代码时,输出将是10和10。很简单。

现在让我们看一下这里的静态变量;我将变量声明为static。

带有静态变量的示例:

public class Variable
{
    public static int i = 5;
    public void test()
    {
        i = i + 5;
        Console.WriteLine(i);
    }
}


public class Exercise
{
    static void Main()
    {
        Variable var1 = new Variable();
        var1.test();
        Variable var2 = new Variable();
        var2.test();
        Console.ReadKey();
    }
}

现在当我运行上述代码时,输出将是1015。因此,静态变量的值在该类的所有实例之间共享。


9
@Pranay: 是的,这样更好,但如果你能给我展示我之前提到的那个例子,那对我和其他人会更好......不管怎样,很好的努力...... - Kartik Patel
1
它是静态的,即其值保持与类一致,并将其值存储在其...等待它...类中。 - chwi
你说静态变量在类的所有实例之间共享...但如果没有实例呢?你还能设置一个变量吗?在静态类中存储动态数据是否被允许? - Kokodoko
@Kokodoko,即使没有实例,你肯定可以设置变量。这定义了它的静态特性。 - Ladmerc

41
C#不支持静态局部变量(即在方法作用域中声明的变量)。 https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members#static-members 但您可以声明静态字段(类成员)。 原因是:静态字段是一种状态,与特定类型的所有实例共享。因此,静态字段的范围是整个类型。这就是为什么您不能在方法内声明静态实例变量 - 方法本身就是一个作用域,而在方法中声明的项必须在该方法的边界上无法访问的原因。

6
在文档中,它被定义为“使用静态修饰符声明的字段称为静态变量。”https://msdn.microsoft.com/en-us/library/aa691162(v=vs.71).aspx 但你在其他解释上是正确的。 - Teoman shipahi
@Teomanshipahi 更新了链接:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#static-and-instance-fields :(该链接介绍了静态变量和实例变量之间的区别)。 - gawkface

22

静态变量用于仅需要一个变量副本的情况。因此,如果您在方法内部声明变量,则没有使用这样的变量,它仅限于函数范围内。

静态变量的示例:

class myclass
{
    public static int a = 0;
}

静态声明的变量通常在一个类的所有实例之间共享。

静态声明的变量通常在一个类的所有实例之间共享。当您创建多个VariableTest类实例时,此变量permanent将被在它们所有实例之间共享。因此,在任何给定时间点,permanent变量中只有一个字符串值。

由于变量的唯一副本可用于所有实例,所以代码this.permament将导致编译错误,因为可以记得this.variablename是指实例变量名称。因此,应直接访问静态变量,如代码中所示。


请问您可以举个例子来解释吗? - Kartik Patel
但是如果我创建一个类的实例,那么我就无法访问静态变量。为什么?我只能通过“类名.变量”来访问静态变量,而不能通过创建类的实例来访问。 - Kartik Patel
@Kartik Patel 因为你必须使用类名来访问静态的 myInt。为什么会这样,我不知道。但我想说,这样更清晰,因为你想要访问类的静态部分,如果需要实例才能访问它,那就不是静态的了。 - dowhilefor
@dowhilefor: 但是正如你上面提到的,“声明为静态变量的变量通常在类的所有实例之间共享。”那么这是什么意思呢? - Kartik Patel
@Kartik Patel,你不能使用实例从外部访问变量,但是你总可以在类内部使用静态变量,这样它就被所有实例所共享。另外,不是我提供了这个答案,我只是对它进行了评论。 - dowhilefor
@dorwhilefor:抱歉,我刚看到你只是在评论它,还没有回答。但是你能否请给我展示一下它的例子? - Kartik Patel

9
一些关于静态变量的“现实世界”例子: 构建一个类,您可以在应用程序中访问硬编码的值。类似于枚举,但数据类型更加灵活。
public static class Enemies
{
    public readonly static Guid Orc = new Guid("{937C145C-D432-4DE2-A08D-6AC6E7F2732C}");
}

广为人知的单例模式,它允许控制一个类只有一个实例。如果你想在整个应用程序中访问它,但不想将它传递给每个类以允许该类使用它,那么这非常有用。

public sealed class TextureManager
    {
        private TextureManager() {}
        public string LoadTexture(string aPath);

        private static TextureManager sInstance = new TextureManager();

        public static TextureManager Instance
        {
            get { return sInstance; }
        }
    }

这是如何调用纹理管理器的方法。
TextureManager.Instance.LoadTexture("myImage.png");

关于你上一个问题:你提到的是编译错误 CS0176。我试图找到更多相关信息,但只能找到msdn所提供的如下内容:
静态方法、字段、属性或事件即使在类的实例未创建时也可以在类上调用。如果创建了任何该类的实例,则它们不能用于访问静态成员。只有一个静态字段和事件的副本存在,并且静态方法和属性只能访问静态字段和静态事件。

9

当只需要一个副本时,使用静态变量。让我通过一个例子来解释:

class circle
{
    public float _PI =3.14F;
    public int Radius;

    public funtionArea(int radius)
    {
        return this.radius * this._PI      
    }
}
class program
{
    public static void main()
    {
        Circle c1 = new Cirle();
        float area1 = c1.functionRaduis(5);
        Circle c2 = new Cirle();
        float area2 = c1.functionRaduis(6);
    }
}

现在,我们已经为我们的class circle创建了2个实例,即创建了2组_PI的副本以及其他变量。所以,如果我们有很多这个类的实例,就会创建多个_PI的副本占用内存。因此,在这种情况下,最好将这样的变量像_PI static一样处理并对其进行操作。

class circle
{
    static float _PI =3.14F;
    public int Radius;

    public funtionArea(int radius)
    {
        return this.radius * Circle._PI      
    }
}
class program
{
    public static void main()
    {
        Circle c1 = new Cirle();
        float area1 = c1.functionRaduis(5);
        Circle c2 = new Cirle();
        float area2 = c1.functionRaduis(6);
    }
}

无论创建多少个“圆形”类的实例,变量“_PI”的仅保存一个副本,从而节省了内存。

你可以在 _PI 变量前加上 readonly,这样任何实例都不能改变它的值。 - Dan
我们如何检查实际保存的内存量?这只是出于我的好奇心,想要验证实际节省的内存量。浮点数占用4个字节,所以如果我有25个对象,那么没有静态关键字的话将会占用100个字节?而使用静态关键字,我只需要4个字节? - Eru

5

静态类不需要创建该类的对象/实例化,您可以在类名前面加上C#关键字static,以使其成为静态类。

请记住:我们不会实例化Console类、String类、Array类。

class Book
{
    public static int myInt = 0;
}

public class Exercise
{
    static void Main()
    {
        Book book = new Book();
       //Use the class name directly to call the property myInt, 
      //don't use the object to access the value of property myInt

        Console.WriteLine(Book.myInt);

        Console.ReadKey();

    }
}

非常好的观察,因为如果您尝试使用对象访问属性myInt的值,则会出现错误: static void Main() { Book book = new Book(); // 这会给您带来错误:book.myInt =5; } - leonidaa

2
作为对“何时使用它?”问题的回应:
我经常使用静态(类)变量来为类的每个实例分配唯一的实例ID。我在每个类中使用相同的代码,非常简单:
//Instance ID ----------------------------------------
    // Class variable holding the last assigned IID
    private static int xID = 0;
    // Lock to make threadsafe (can omit if single-threaded)
    private static object xIDLock = new object();
    // Private class method to return the next unique IID 
    //  - accessible only to instances of the class
    private static int NextIID()                    
    {
        lock (xIDLock) { return ++xID; }
    }
    // Public class method to report the last IID used 
    // (i.e. the number of instances created)
    public static int LastIID() { return xID; }
    // Instance readonly property containing the unique instance ID
    public readonly int IID = NextIID();
//-----------------------------------------------------

这段文字说明了静态变量和方法的几个要点:
  1. 静态变量和方法与类相关,而不是特定实例。
  2. 在实例构造函数中可以调用静态方法 - 在此情况下,静态方法 NextIID 用于初始化只读属性 IID,该属性是此实例的唯一标识符。
我认为这很有用,因为我开发的应用程序使用大量对象,能够跟踪创建了多少对象以及跟踪/查询各个实例是很好的。
我还使用类变量来跟踪实例属性的总数和平均值,这些信息可以实时报告。我认为类是保存有关类的所有实例的摘要信息的好地方。

1
数据成员和操作类型实例的函数成员被称为实例成员。例如 int 的 ToString 方法就是实例成员的例子。默认情况下,成员都是实例成员。 不操作类型实例而是操作类型本身的数据成员和函数成员必须标记为静态。Test.Main 和 Console.WriteLine 方法是静态方法。Console 类实际上是一个静态类,这意味着它的所有成员都是静态的。您从未创建过 Console 的实例——一个控制台在整个应用程序中共享。

0
您不需要实例化一个对象,因为您将使用静态变量: Console.WriteLine(Book.myInt);

0

尝试使用类名 Book.myInt 直接调用它。


准确地说,参见上面@Kunal Mukherjee的示例。 - leonidaa

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