static关键字可以用在哪方面

 

  • 如果对类应用 static 关键字,则该类的所有成员都必须是静态的。

 

引用《C#语言参考》 

Members of a class are either static members or instance members. Generally speaking, it is useful to think of static members as belonging to class types and instance members as belonging to objects (instances of class types).

When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. In addition, a constant or type declaration implicitly declares a static member. Static members have the following characteristics:

 

1.When a static member M is referenced in a member-access  of the form E.M, E must denote a type containing M. It is a compile-time error for E to denote an instance.

意思是说: 如果代码为“E.M”,那就说E一定是一个类型标志符,如果E代表一个实例变量,那么编译时一定会报错。

2.A static field identifies exactly one storage location to be shared by all instances of a given closed class type. No matter how many instances of a given closed class type are created, there is only ever one copy of a static field.

意思是说:静态字段只会有一份存储。

3.A static function member (method, property, event, operator, or constructor) does not operate on a specific instance, and it is a compile-time error to refer to this in such a function member.

意思是说:静态成员不能操作一个实例,在静态方法中是不允许出this这样的符号,否则会编译错误。 从逻辑思维的角度来看,这个

是正常的,因为静态方法并不属于任何实例,这点从第一点就知道,所以它并不知道this指的是哪个实例。 

 

Static constructors are not inherited, and cannot be called directly.


The static constructor for a closed class type executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

1.An instance of the class type is created.
2.Any of the static members of the class type are referenced. 注意:抽象类是可以有静态字段,所以也就可以有类型 
构造器,CLR并不反对接口是不能有静态字段,但是与CLS兼容的接口类型不允许有任何静态成员,实现上C#阻止在接口中定义静态成员。(奇怪的是Java允许定义静态字段,但不允许有静态方法。但是:  An official proposal was made to add static methods to interfaces in Java 7

If a class contains the Main method in which execution begins, the static constructor for that class executes before the Main method is called. 

 

The example

using System;

class Test
{
       static void Main() {
              A.F();
              B.F();
       }
}

class A
{
       static A() {
              Console.WriteLine("Init A");
       }
       public static void F() {
              Console.WriteLine("A.F");
       }
}

class B
{
       static B() {
              Console.WriteLine("Init B");
       }
       public static void F() {
              Console.WriteLine("B.F");
       }
}

must produce the output:

Init A
A.F
Init B
B.F


Another Important example

using System;

class A
{
       public static int X;

       static A() {
              X = B.Y + 1;
       }
}

class B
{
       public static int Y = A.X + 1;

       static B() {}

       static void Main() {
              Console.WriteLine("X = {0}, Y = {1}", A.X, B.Y);
       }
}

produces the output

X = 1, Y = 2


上面这个代码你不觉得奇怪吗?按一般的理解,两个类的构造器貌似出现了循环引用。 但结果并没有出现死循环。

(关于这一点我在下面有更贴切、详细的说明。)

 

To execute the Main method, the system first runs the initializer for B.Y, prior to class B’s static constructor. Y’s initializer causes A’s static constructor to be run because the value of A.X is referenced. The static constructor of A in turn proceeds to compute the value of X, and in doing so fetches the default value of Y, which is zero. A.X is thus initialized to 1. The process of running A’s static field initializers and static constructor then completes, returning to the calculation of the initial value of Y, the result of which becomes 2.

 

 

多线程的情况下访问类构造器 

The CLR guarantees a type initializer (static constructor) will execute only once for any given type. This simplifies my life, because I don’t need to worry about multiple threads inside of a static constructor. Take for example the following program:
static void Main(string[] args)
{
   Thread threadA = new Thread(new ThreadStart(TouchFoo));
   threadA.Name = "Thread A";
 
   Thread threadB = new Thread(new ThreadStart(TouchFoo));
   threadB.Name = "Thread B";
 
   threadA.Start();
   threadB.Start();
   
   threadA.Join();
   threadB.Join();
}
 
static void TouchFoo()
{
   Foo.SayHello();
}
 
class Foo
{
   static Foo()
   {
      Thread.Sleep(1000);
      Console.WriteLine("Foo .cctor on thread {0}", Thread.CurrentThread.Name);
   }
 
   static public void SayHello()
   {
      Console.WriteLine("Hello From Foo");
   }
}
In this program, Thread A will probably get the first chance to execute TouchFoo. In doing so, the CLR recognizes the static constructor for Foo has not been executed and sends Thread A into the constructor, which puts the thread to sleep for 1000 ms. The sleeping period allows Thread B to run. Thread B arrives inside of TouchFoo, and again the CLR recognizes the static constructor for Foo has not completed executing. This time however, the CLR knows somebody is working inside of the constructor, and it blocks Thread B until Thread A finishes Foo’s constructor. Typically then, this program would produce the following output:
Foo .cctor on thread Thread A
Hello From Foo on thread Thread A
Hello From Foo on thread Thread B
No matter how many threads we throw into the mix, the CLR will only allow one thread inside of the static constructor. Of course this isn’t magic, there has to be some thread synchronization (a lock) taken by the CLR before entering the static constructor. 重点在这里
Where there are locks, there is the possibility of a deadlock. So what happens if we try to put the runtime into a bind by feeding it code that should deadlock? Do you think the following program will execute, or will I need to kill it from task manager?
class Class1
{
   [STAThread]
   static void Main(string[] args)
   {
      Thread threadA = new Thread(new ThreadStart(TouchFoo));
      threadA.Name = "Thread A";
      
      Thread threadB = new Thread(new ThreadStart(TouchBar));
      threadB.Name = "Thread B";
 
      threadA.Start();
      threadB.Start();
 
      threadA.Join();
      threadB.Join();
   }
 
   static void TouchFoo()
   {
      string s = Foo.Message;
   }
 
   static void TouchBar()
   {
      string s = Bar.Message;
   }
}
 
class Foo
{
   static Foo()
   {
      Console.WriteLine("Begin Foo .cctor on thread {0}", Thread.CurrentThread.Name);
      Thread.Sleep(5000);
      Console.WriteLine("Foo has a message from Bar: {0}", Bar.Message);         
      message = "Hello From Foo";
      Console.WriteLine("Exit Foo .cctor on thread {0}", Thread.CurrentThread.Name);
   }
 
   static public string Message
   {
      get { return message; }
   }
 
   static string message = "blank";
}
 
class Bar
{
   static Bar()
   {
      Console.WriteLine("Begin Bar .cctor on thread {0}", Thread.CurrentThread.Name);
      Thread.Sleep(5000);
      Console.WriteLine("Bar has a message from Foo: {0}", Foo.Message);         
      message = "Hello From Bar";
      Console.WriteLine("Exit Bar .cctor on thread {0}", Thread.CurrentThread.Name);
   }
 
   static public string Message
   {
      get { return message; }
   }
 
   static string message = "empty";    
}
Notice in this example, the static constructor for Foo references Bar’s Message property, and vice versa. This produces the following scenario:
Thread A starts and eventually enters the static constructor for Foo. After a writing to the screen, the thread goes to sleep for 5000 ms. Thread B now has a chance to run and eventually enters the constructor for Bar, prints a message and goes to sleep.
Next, Thread A wakes up and finds it needs information from an un-initialized Bar, but it cannot run the constructor because Thread B has it locked. Thread B awakes and needs information from Foo, but it cannot run the Foo constructor because Thread A has it locked. Thread A needs the lock held by Thread B, and Thread B needs the lock held by Thread A. Classic deadlock!
As it turns out, the CLI specification addresses this issue in section 9.5.3.3 of Partition II, entitled “Races and Deadlocks”:
Type initialization alone shall not create a deadlock unless some code called from a type initializer (directly or indirectly) explicitly invokes blocking operations.
So if we don’t have a deadlock, what happens? Here is the output on my machine:
Begin Foo .cctor on thread Thread A
Begin Bar .cctor on thread Thread B
Bar has a message from Foo: blank
Exit Bar .cctor on thread Thread B
Foo has a message from Bar: Hello From Bar
Exit Foo .cctor on thread Thread A
The message Bar retrieves from Foo is “blank”, but Foo was supposed to initialize the message to “Hello From Foo”. The runtime allowed Thread B to reference Foo before Foo’s static constructor completed! 重点在这里 
 It could have just as easily allowed Thread A to reference Bar before Bar’s static constructor completed, but by letting just one of the threads through, a lock became free and we avoid a deadlock. The runtime cannot perform miracles and let both the constructors run to completion.

The morale of the story is: never touch the static member of another type inside of a static constructor. 

上面的意思是:其它上面的程序并没有让程序死锁,我指的是非得要在任务管理器结束任务才能使程序结束,

CLR只是让其中一个线程提前结束,实际上这个线程并没有完成类构造器的运行,只有这样才能释放其中一个锁,避免了死锁。 

但上面提到是也有可能让另一个线程在运行完类构造器前,也提前释放锁。(这里我不知道我是否理解正确,欢迎指正,谢谢!)

你可能感兴趣的:(static)