Hiểu từ khóa tĩnh


15

Tôi có một số kinh nghiệm trong việc phát triển với Java, Javascript và PHP.

Tôi đang đọc Microsoft Visual C # 2010 Từng bước mà tôi cảm thấy đây là một cuốn sách rất hay về giới thiệu cho bạn ngôn ngữ C #.

Tôi dường như có vấn đề trong việc hiểu từ khóa tĩnh. Từ những gì tôi hiểu được cho đến nay nếu một lớp được khai báo tĩnh, tất cả các phương thức và biến phải là tĩnh. Phương thức chính luôn là một phương thức tĩnh, vì vậy trong lớp mà phương thức chính tồn tại tất cả các biến và phương thức được khai báo là tĩnh nếu bạn phải gọi chúng trong phương thức chính. Ngoài ra tôi đã nhận thấy rằng để gọi một phương thức tĩnh từ một lớp khác, bạn không cần phải tạo một đối tượng mà bạn có thể sử dụng tên lớp.

Nhưng mục đích thực sự của từ khóa tĩnh là gì? Khi nào tôi nên khai báo biến tĩnh và phương thức?


4
static trong C # gần giống như static trong Java. Nếu bạn hiểu nó bằng Java, bạn sẽ không gặp bất kỳ vấn đề nào trong C #
superM

Java là ngôn ngữ lập trình đầu tiên của tôi và tôi cũng không hiểu khái niệm này. Tôi chỉ sử dụng Java trong một khoảng thời gian ngắn
Nistor Alexandru

Tóm lại: sử dụng "tĩnh" khi bạn không cần định hướng đối tượng, ví dụ, chỉ một số phương thức hoặc biến độc lập. Khai báo một lớp là tĩnh có nghĩa là đặt các hàm và biến không hướng đối tượng đó chỉ trong một tên chung (dấu cách), tên lớp.
Doc Brown

Câu trả lời:


14

Từ khóa 'tĩnh' trong C # đang đề cập đến một cái gì đó trong lớp hoặc chính lớp đó, được chia sẻ giữa tất cả các phiên bản của lớp. Ví dụ, một trường được đánh dấu là tĩnh có thể được truy cập từ tất cả các phiên bản của lớp đó thông qua tên lớp.

public class SomeObject
{
    //Static Field
    static int Foo = 3;

    //instance field
    private int _Foo2 = 4;

    //instance property
    public int Foo2{get{return _Foo2;}set{_Foo2 = value;}}


    //static factory method
    public static SomeObject CreateSomeObject(int fooValue)
    {
        SomeObject retVal = new SomeObject();
        retVal.Foo2 = fooValue;
        return retVal;
    }

    //Parameterless instance constructor
    public SomeObject()
    {
    }

    public static int Add(int x)
    {
        //Static methods can only deal with local variables, or fields that
        //  are also static in the class.  This one adds x to the static member foo
        return x + Foo;

        //Foo2 is not accessable here!
    }

      //Instance method
    public int AddSomething(int x)
    {
        //Add x to the property value of Foo2
        return x + this.Foo2;

        //Note that Foo *is* accessable here as 'SomeObject.Foo'
    }

}

Tôi có thể thành thật nói rằng tôi chưa bao giờ sử dụng một lớp được đánh dấu là tĩnh ngoại trừ việc tạo các phương thức mở rộng ( Hướng dẫn nhanh về các phương thức mở rộng ).

Dù sao, có các mẫu thiết kế cụ thể để sử dụng các phương thức tĩnh, chẳng hạn như mẫu nhà máymẫu đơn , nhưng điều quan trọng cần nhớ là các phương thức tĩnh và hàm tạo không xử lý bất kỳ trường hợp cụ thể nào của lớp (trừ khi bạn truyền vào một) thông thường để làm tính toán hoặc để so sánh giữa các đối tượng. Phương thức "Chính" mà bạn đang đề cập đến luôn luôn tĩnh, nhưng để xem nó từ một quan điểm khác, hãy xem bài viết này .

Để theo dõi điều này, đây là cách khác biệt giữa các phương thức tĩnh, tức thời và các thuộc tính được gọi.

public static void Main(string[] args)
{
    //This is a static method that starts a thread in an application
    // space.  At this point not everything actually has to be static...

    //Here is an instantiation with a parameterless contruction
    SomeObject obj = new SomeObject();

    //Here is an instantiation using a static factory method
    SomeObject obj2 = SomeObject.CreateSomeObject(3);

    //Getting field value from static field
    // Notice that this references the class name, not an instance
    int fooValue1 = SomeObject.Foo;

    //Getting property value from instance
    //  Note that this references an object instance
    int fooValue2 = obj2.Foo2;

    //Instance method must be called through an object
    obj2.AddSomething(4);  //if default constructor, would return 8

    //Static methods must be called through class name
    SomeObject.Add(4); //Returns 7
}

Ngoài ra, kiểm tra bài đăng này để có cái nhìn sâu hơn về các lớp tĩnh.


18

Đây là cách giải thích của Joshua Bloch, điều mà tôi thấy tuyệt vời như hầu hết những gì anh ta nói (vâng tôi là một fan hâm mộ Joshua Bloch :)). Điều này được trích dẫn từ bộ nhớ.

Hãy tưởng tượng rằng một lớp học tương đương với một bản in màu xanh cho một ngôi nhà. Hãy tưởng tượng rằng một ngôi nhà được in màu xanh lam như là một ví dụ của lớp dành cho lớp. Bạn có thể có một lớp (in màu xanh) và nhiều trường hợp (nhà) được tạo từ nó.

Bây giờ, thông thường chỉ ra rằng hầu hết các chức năng / hành vi mà một ngôi nhà (ví dụ) có thể có / làm, mặc dù chúng được khai báo trong bản in màu xanh, không thể được sử dụng cho đến khi một ngôi nhà thực tế được tạo ra từ màu xanh đó -print (lớp). Giống như, bản in màu xanh của bạn có thể chứa trong đó nơi chuyển đổi ánh sáng và bóng đèn, nhưng bạn không có cách nào để chúng hoạt động trên bản in màu xanh, bạn phải thực sự xây dựng ngôi nhà để có thể để bật và tắt công tắc đèn và bật và tắt một số bóng đèn nhất định.

Tuy nhiên, bạn có thể có một số hành vi có thể áp dụng trực tiếp cho bản in màu xanh và bạn có thể sử dụng / truy cập trực tiếp trên bản in màu xanh mà không cần phải tạo một ngôi nhà thực sự từ bản in màu xanh đó. Hãy tưởng tượng rằng bản in màu xanh của bạn có một nút, khi nhấn, sẽ hiển thị dấu chân của ngôi nhà có trong bản in màu xanh đó (bằng cách tính tất cả các chiều dài của các bức tường và như vậy). Rõ ràng là bạn CÓ THỂ xây dựng một ngôi nhà trước, sau đó đi vòng quanh đo dấu chân của nó, nhưng bạn có thể làm điều này với bản in màu xanh một mình, vì vậy sẽ hữu ích hơn khi thực hiện hành vi này trong bản in màu xanh. Một nút nhúng in màu xanh như vậy để tính toán dấu chân của ngôi nhà tương đương với việc có một hàm tĩnh trong một lớp.


Hoặc bạn sẽ có một Blueprintlớp thực hiện chức năng của bản thiết kế, bao gồm khả năng tính toán dấu chân của ngôi nhà được thể hiện bằng bản thiết kế. Ví dụ kế hoạch chi tiết này sau đó được đưa đến một Builder(lần lượt có khả năng là một thể hiện), đến lượt nó thực hiện những gì cần thiết để xây dựng và đưa ra một số lượng các Buildingtrường hợp có thể tùy ý dựa trên một kế hoạch chi tiết.
một CVn

11

Nhìn vào nó theo cách này giúp tôi:

  • Mỗi loại có một thể hiện tĩnh.
  • Thể hiện tĩnh được tạo tại lần đầu tiên bạn truy cập loại - thông qua thể hiện tĩnh hoặc tạo một thể hiện khác.
  • Bạn có thể tạo bao nhiêu trường hợp không tĩnh tùy thích, nhưng chỉ có một thể hiện tĩnh.
  • Bất cứ điều gì trong một lớp được khai báo là tĩnh đều thuộc về thể hiện tĩnh và do đó không có quyền truy cập vào bất kỳ trường hợp nào khác mà bạn tạo. Nhưng các thể hiện khác DO có quyền truy cập vào thể hiện tĩnh.
  • Nếu một lớp được khai báo là tĩnh thì bạn không thể tạo các thể hiện khác, chỉ có thể hiện tĩnh.
  • Bạn có thể khai báo hàm tạo tĩnh cho thể hiện tĩnh giống như hàm tạo đối với thể hiện bình thường (nhưng bằng cách khai báo tĩnh).

Khi nào nên sử dụng từ khóa tĩnh:

  • Bất kỳ phương thức nào không cần truy cập vào các thuộc tính cục bộ đều có thể và có thể được khai báo là tĩnh.
  • Các lớp trợ giúp không có bất kỳ trạng thái nào (dù sao cũng hiếm) và điều đó sẽ không bao giờ bị chế giễu có thể được khai báo là tĩnh. Cho dù họ nên là một vấn đề khác; sử dụng chức năng này một cách tiết kiệm.
  • Các thuộc tính và các trường phải được truy cập bởi tất cả các thể hiện của một lớp phải được khai báo là tĩnh. Nhưng chỉ sử dụng điều này khi không có lựa chọn khác.

+1, để tóm tắt tốt, tôi không biết rằng mỗi loại có 1 thể hiện tĩnh và tôi thấy thật lạ khi nói với bạn sự thật.
NoChance

2
@EmmadKareem Đó chỉ là một mô hình tinh thần mà pdr sử dụng, bởi vì "Looking at it this way helps"anh ta. Bạn thấy nó lạ bởi vì nó không chính xác, nhưng bạn có thể nghĩ về nó như thế nếu bạn muốn. Bạn có biết mô hình Bohr? Đây là một bộ quy tắc và ý tưởng về cách các nguyên tử và electron tương tác với nhau. Các mô hình làm việc tùy thuộc vào những gì bạn làm, nhưng nó không phải là thực tế.
phant0m

@ phant0m, cảm ơn vì lời giải thích, tôi đã có ấn tượng rằng nó thực sự không phải là một mô hình và tôi đã rất ngạc nhiên vì điều đó.
NoChance

Trên thực tế, đôi khi có những lý do bạn có thể không muốn thực hiện một phương thức staticngay cả khi không sử dụng các thuộc tính cục bộ. Làm cho mọi thứ staticcó thể thêm khớp nối cho khách hàng vì họ phải giải quyết trực tiếp lớp. Ví dụ, điều này có thể gây khó khăn hơn cho việc kiểm tra đơn vị w / mocking.
Allan

@ ALLan: Có thể cho rằng, nếu bạn đang gọi một phương thức công khai trên một lớp không ảnh hưởng đến trạng thái của một thể hiện của lớp đó, thì NÊN tĩnh, để làm rõ điều đó cho nhà phát triển ứng dụng khách. Nếu phương pháp đó thực hiện nhiều đến mức nó cần chế nhạo, thì đó là một vấn đề khác có thể được giải quyết bằng nhiều cách khác nhau.
pdr

3

Giải thích đơn giản nhất --- Tĩnh => Chỉ có một bản sao sẽ tồn tại trên mỗi môi trường.

Vì vậy, trong VM hoặc CLR, sẽ chỉ có một bản sao của một lớp tĩnh và, bất kỳ lớp nào khác tham chiếu nó sẽ phải chia sẻ các phương thức và dữ liệu của nó với tất cả các lớp khác tham chiếu nó.

Đối với một biến tĩnh, sẽ chỉ có một phiên bản của biến này trong môi trường thời gian chạy cho dù có bao nhiêu bản sao của lớp sở hữu được tạo khi chúng tham chiếu một biến tĩnh, tất cả chúng sẽ tham chiếu cùng một phần lưu trữ.


1

Các thành viên tĩnh được liên kết với Lớp, không phải với bất kỳ phiên bản nào của Lớp đó.

Vì chúng ta đang nói về .Net, hãy xem xét lớp String , đặc biệt là ChiaTham gia phương thức .

Chia là một phương thức ví dụ . Tạo một biến Chuỗi, cung cấp cho nó một giá trị và bạn có thể gọi Split () trên biến / giá trị đó và lấy lại một mảng "bit":

String s1 = "abc,def,ghi" ; 
String[] array2 = s1.Split( ',' ) ; 

Vì vậy, ví dụ phương pháp, giá trị tổ chức trong trường hợp lớp học cho các vấn đề .

Tham gia là một phương pháp tĩnh . OK, nó mang lại kết quả Chuỗi khi được cung cấp một dấu phân cách và mảng Chuỗi để nhai, do đó, đó là "điều cần làm với" Lớp Chuỗi, nhưng nó không được liên kết với bất kỳ giá trị cụ thể nào trong bất kỳ trường hợp Chuỗi nào (thực sự, các giá trị thể hiện là không có sẵn cho các phương thức tĩnh).
Trong các ngôn ngữ khác, phương thức Tham gia có thể đã bị "mắc kẹt" Lớp Array (hoặc, có lẽ tốt hơn, Lớp StringArray) nhưng Bạn bè của chúng tôi ở Redmond đã quyết định rằng nó "phù hợp" hơn với lớp String, vì vậy họ đã đặt nó ở đó .

String[] array3 = { ... } 
s1 = String.Join( array3, "," ) ; 

Một cách khác có thể là có một phương thức Tham gia thể hiện , trong đó giá trị được giữ trong Chuỗi [thể hiện lớp] mà chúng tôi đã sử dụng làm dấu phân cách nối, đại loại như:

// Maybe one day ... 
String s4 = "," ; 
s1 = s4.Join( array3 ) ; 

1

Các static từ khóa có thể là một chút khó khăn cho người mới để nắm bắt. Mục đích chính của nó là xác định một thành viên lớp không thuộc về bất kỳ trường hợp đơn lẻ nào của lớp, mà thay vào đó là chính lớp đó.

Không đi sâu vào chi tiết, C # (và Java) thực thi một cách cứng nhắc lý tưởng hướng đối tượng rằng tất cả mã và dữ liệu phải thuộc về một đối tượng và do đó bị giới hạn về phạm vi, khả năng hiển thị và thời gian tồn tại. Đó thường là cách thực hành tốt nhất ở bất cứ nơi nào nguyên lý cơ bản của một đối tượng đại diện cho một số điều trong thế giới thực. Tuy nhiên, không phải lúc nào cũng vậy; đôi khi những gì bạn cần là một hàm hoặc biến mà bạn có thể nhận được từ bất kỳ nơi nào trong mã, mà không yêu cầu bạn chuyển một tham chiếu đến một đối tượng có chứa nó và với sự đảm bảo rằng dữ liệu bạn đang xem hoặc thay đổi là chính xác mọi người khác là xử lý, và không phải là một bản sao của nó thuộc về một thể hiện khác của một đối tượng.

Hành vi như vậy đã có sẵn trong C và C ++ dưới dạng hàm hoặc biến "toàn cầu", không được gói gọn trong một đối tượng. Vì vậy, như một sự thỏa hiệp, C # và Java hỗ trợ "phạm vi tĩnh", một nửa điểm giữa mã thực sự toàn cầu không có đối tượng cha và các thành viên thể hiện phạm vi giới hạn.

Bất kỳ "thành viên mã" nào (hàm, thuộc tính, trường) được khai báo là staticthuộc phạm vi của dòng đầu tiên của main()hàm và không để lại cho đến khi main()hàm kết thúc. Trong tiếng Anh đơn giản, một thành viên tĩnh tồn tại và có thể được sử dụng miễn là chương trình đang chạy. Ngoài ra, các thành viên tĩnh được gọi bằng cách gọi chúng là thành viên của chính loại đó, không phải là thành viên của bất kỳ một thể hiện nào của loại đó:

public class Foo
{
   public int MyInt {get;set;} //this is an "instance member"
   public static int MyStaticInt {get;set;} //this is a "static member"
}

...

var myFoo = new Foo();
myFoo.MyInt = 5; //valid
myFoo.MyStaticInt = 5; //invalid; MyStaticInt doesn't belong to any one Foo

Foo.MyInt = 5; //invalid; MyInt only has meaning in the context of an instance
Foo.MyStaticInt = 2; //valid

Điều này làm cho các thành viên tĩnh hiển thị với bất kỳ mã nào có kiến ​​thức về loại, cho dù họ có biết về bất kỳ trường hợp nào của nó hay không.

Để trả lời câu hỏi của bạn, lợi ích chính của việc đánh dấu một cái gì đó là tĩnh là nó sẽ hiển thị ở bất cứ nơi nào loại chính nó được biết, bất kể mã tiêu thụ có hoặc có thể lấy một thể hiện của đối tượng chứa. Cũng có một chút lợi ích hiệu suất ; bởi vì phương thức nằm trong phạm vi tĩnh, nó chỉ có thể truy cập các thành viên tĩnh khác (cùng lớp hoặc các lớp khác) và bất cứ điều gì được truyền vào dưới dạng tham số. Do đó, bộ thực thi không phải giải quyết bất kỳ tham chiếu nào đến thể hiện hiện tại của đối tượng chứa, vì thông thường nó sẽ phải cho một phương thức cá thể để cung cấp thông tin trạng thái cụ thể theo ngữ cảnh.

Toàn bộ các lớp cũng có thể được đánh dấu tĩnh; bằng cách làm như vậy, bạn nói với trình biên dịch rằng khai báo lớp sẽ chỉ bao gồm các thành viên tĩnh và do đó không thể được khởi tạo. Đây là một cách dễ dàng để đảm bảo có một và chỉ một bản sao của một đối tượng trong bộ nhớ; làm cho lớp và mọi thứ trong đó tĩnh. Tuy nhiên, rất hiếm khi đây là giải pháp tốt nhất cho nhu cầu như vậy. Trong tình huống yêu cầu chính xác một bản sao của một tập hợp dữ liệu, "đơn lẻ" thường được ủng hộ thay thế; đây là một lớp không tĩnh, sử dụng một bộ truy cập tĩnh và một hàm tạo không công khai để cung cấp quyền truy cập vào một thể hiện của chính nó. Về mặt lý thuyết, một singleton cung cấp nhiều lợi ích tương tự của một lớp hoàn toàn tĩnh, nhưng với khả năng bổ sung để sử dụng lớp theo cách hướng đối tượng, dựa trên cá thể.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.