2009-04-21 65 views
182

Tôi có một số lớp học không thực sự cần bất kỳ trạng thái nào. Từ quan điểm tổ chức, tôi muốn đưa chúng vào hệ thống phân cấp.Tại sao tôi không thể kế thừa các lớp tĩnh?

Nhưng có vẻ như tôi không thể khai báo thừa kế cho các lớp tĩnh.

Something như thế:

public static class Base 
{ 
} 

public static class Inherited : Base 
{ 
} 

sẽ không hoạt động.

Tại sao các nhà thiết kế ngôn ngữ lại đóng khả năng đó?

+0

có thể trùng lặp của [Làm thế nào có thể một lớp tĩnh xuất phát từ một đối tượng?] (http://stackoverflow.com/questions/556203/how-can-a-static-class-derive-from-an-object) –

Trả lời

148

lời trích dẫn từ here:

Đây thực sự là do thiết kế. Có vẻ như không có lý do chính đáng để thừa kế một lớp tĩnh. Nó có các thành viên tĩnh công cộng mà bạn luôn có thể truy cập thông qua tên lớp. Lý do duy nhất tôi đã thấy để kế thừa nội dung tĩnh là những nội dung xấu, chẳng hạn như lưu một vài ký tự gõ.

Có thể có lý do để xem xét cơ chế đưa thành viên tĩnh trực tiếp vào phạm vi (và chúng tôi sẽ xem xét điều này sau chu kỳ sản phẩm Orcas), nhưng thừa kế lớp tĩnh không phải là cách để đi: để sử dụng và chỉ hoạt động cho các thành viên tĩnh xảy ra trong một lớp tĩnh.

(Mads Torgersen, C# Ngôn Ngữ PM)

ý kiến ​​khác từ channel9

Thừa kế trong .NET chỉ hoạt động trên cơ sở ví dụ. Các phương thức tĩnh được định nghĩa ở mức loại không ở cấp độ cá thể. Đó là lý do tại sao ghi đè không hoạt động với các phương thức/thuộc tính/sự kiện tĩnh ...

Phương pháp tĩnh chỉ được lưu giữ một lần trong bộ nhớ. Không có bảng ảo, vv được tạo ra cho chúng.

Nếu bạn gọi phương thức mẫu trong .NET, bạn luôn cung cấp cho nó phiên bản hiện tại. Điều này bị ẩn bởi thời gian chạy .NET, nhưng nó xảy ra. Mỗi phương thức thể hiện có tham số đầu tiên là một con trỏ (tham chiếu) đến đối tượng mà phương thức đang chạy. Điều này không xảy ra với các phương thức tĩnh (vì chúng được định nghĩa ở mức loại). Trình biên dịch nên quyết định chọn phương thức để gọi như thế nào?

(littleguru)

Và như một ý tưởng có giá trị, littleguru có một "cách giải quyết" một phần cho vấn đề này: mô hình Singleton.

+1

Với trích dẫn đầu tiên của bạn, bạn có thể tạo một lớp cá thể làm lớp cơ sở và sau đó sử dụng các thành viên công khai của nó. –

+62

Thiếu trí tưởng tượng về phía Torgersen, thực sự. Tôi đã có một lý do khá tốt cho việc muốn làm điều này :) – Thorarin

+2

bạn vui lòng chia sẻ nó? – boj

1

Hmmm ... nó sẽ khác nhiều nếu bạn chỉ có các lớp không tĩnh chứa đầy các phương thức tĩnh ..?

+2

Đó là những gì còn lại với tôi. Tôi làm theo cách này. Và tôi không thích nó. – User

+0

Tôi đã thực hiện nó theo cách này là tốt, nhưng đối với một số lý do nó không cảm thấy thẩm mỹ khi bạn biết bạn sẽ không bao giờ nhanh chóng đối tượng. Tôi luôn luôn đau đớn về các chi tiết như thế này, mặc dù thực tế là không có chi phí vật chất của nó. – gdbj

20

Hãy suy nghĩ về nó theo cách này: bạn truy cập vào các thành viên tĩnh thông qua tên loại, như thế này:

MyStaticType.MyStaticMember(); 

Were bạn kế thừa từ lớp đó, bạn sẽ phải truy cập vào nó thông qua tên kiểu mới:

MyNewType.MyStaticMember(); 

Vì vậy, mục mới không có mối quan hệ với bản gốc khi được sử dụng trong mã. Sẽ không có cách nào để tận dụng lợi thế của bất kỳ mối quan hệ thừa kế nào cho những thứ như đa hình.

Có lẽ bạn đang nghĩ bạn chỉ muốn mở rộng một số mục trong lớp gốc. Trong trường hợp đó, không có gì ngăn cản bạn chỉ sử dụng một thành viên của bản gốc trong một loại hoàn toàn mới.

Có lẽ bạn muốn thêm phương thức vào loại tĩnh hiện có. Bạn có thể thực hiện điều đó thông qua các phương thức mở rộng.

Có lẽ bạn muốn có thể vượt qua một static Type đến một hàm trong thời gian chạy và gọi một phương thức trên loại đó, mà không biết chính xác những gì phương thức thực hiện. Trong trường hợp đó, bạn có thể sử dụng Giao diện.

Vì vậy, cuối cùng bạn không thực sự đạt được bất kỳ thứ gì từ kế thừa các lớp tĩnh.

+5

Bạn không thể thêm phương thức vào một kiểu tĩnh hiện có thông qua một phương thức mở rộng. Xin vui lòng xem bình luận của tôi về câu trả lời được chấp nhận cho một ví dụ mong muốn sử dụng. (Về cơ bản bạn sẽ muốn đổi tên những gì bạn đã đặt tên MyNewType thành MyStaticType, vì vậy MyStaticType: OldProject.MyStaticType) – user420667

+2

Có thể xác định quan hệ thừa kế với kiểu tĩnh và thành viên tĩnh ảo theo cách mà 'SomeClass trong đó T: Foo 'có thể truy cập các thành viên của' Foo', và nếu 'T' là một lớp vượt qua một số thành viên tĩnh ảo của' Foo', lớp chung sẽ sử dụng những phần ghi đè đó. Nó có thể thậm chí có thể xác định một quy ước thông qua ngôn ngữ có thể làm như vậy trong một thời trang tương thích với CLR hiện tại (ví dụ: một lớp với các thành viên như vậy nên xác định một lớp không tĩnh bảo vệ có chứa các thành viên dụ như vậy, cùng với một trường tĩnh giữ một thể hiện kiểu đó). – supercat

61

Lý do chính mà bạn không thể kế thừa một lớp tĩnh là chúng trừu tượng và bị đóng dấu (điều này cũng ngăn cản bất kỳ trường hợp nào của chúng được tạo ra).

Vì vậy, đây:

static class Foo { } 

biên dịch để IL này:

.class private abstract auto ansi sealed beforefieldinit Foo 
    extends [mscorlib]System.Object 
{ 
} 
+13

Bạn đang nói rằng tĩnh được thực hiện như trừu tượng + niêm phong. Anh ấy muốn biết * tại sao * điều này đã được thực hiện. Tại sao nó được niêm phong? – Lucas

+20

Điều này không trả lời tất cả câu hỏi; nó chỉ giải quyết vấn đề anh ta hỏi. –

+24

Vâng, OP nên hỏi "Tại sao các lớp tĩnh được đóng dấu", không phải "Tại sao tôi không thể kế thừa từ các lớp tĩnh?", Câu trả lời là tất nhiên * "bởi vì chúng được đóng dấu" *. Câu trả lời này nhận được phiếu bầu của tôi. –

3

gì bạn muốn đạt được bằng cách sử dụng lớp thứ bậc có thể đạt được chỉ thông qua không gian tên. Vì vậy, các ngôn ngữ hỗ trợ namespapces (như C#) sẽ không sử dụng việc triển khai phân cấp lớp của các lớp tĩnh. Vì bạn không thể khởi tạo bất kỳ lớp nào, tất cả những gì bạn cần là một tổ chức phân cấp các định nghĩa lớp mà bạn có thể có được thông qua việc sử dụng các không gian tên

+1

+1 cho tổ chức thông qua không gian tên, không kế thừa. – Lucas

+0

Tôi có trình tải chung để nhận lớp là loại. Lớp đó có 5 phương thức trợ giúp và 2 phương thức trả về một chuỗi (tên và mô tả). Tôi có thể đã chọn nó sai (tôi nghĩ như vậy), nhưng giải pháp duy nhất tôi tìm thấy là để nhanh chóng lớp trong bộ tải chung, để truy cập các phương pháp ... meh. – ANeves

+0

Thay thế sẽ là một từ điển khổng lồ, tôi đoán vậy. Ngoài ra, khi bạn nói "những gì bạn muốn đạt được", bạn nên nói "những gì bạn dường như đang nhắm đến" hoặc một cái gì đó tương tự. – ANeves

2

Mặc dù bạn có thể truy cập các thành viên tĩnh "kế thừa" thông qua tên lớp được thừa kế, thành viên tĩnh không thực sự được kế thừa. Đây là một phần lý do tại sao chúng không thể ảo hoặc trừu tượng và không thể bị ghi đè. Trong ví dụ của bạn, nếu bạn khai báo một Base.Method(), trình biên dịch sẽ ánh xạ một cuộc gọi đến Inherited.Method() trở lại Base.Method(). Bạn cũng có thể gọi Base.Method() một cách rõ ràng. Bạn có thể viết một bài kiểm tra nhỏ và xem kết quả với Reflector. Vì vậy ... nếu bạn không thể kế thừa các thành viên tĩnh, và nếu các lớp tĩnh có thể chứa chỉ thành viên tĩnh, những gì tốt sẽ kế thừa một lớp tĩnh làm gì?

3

Bạn có thể sử dụng bố cục thay thế ... điều này sẽ cho phép bạn truy cập các đối tượng lớp từ loại tĩnh. Nhưng vẫn không thể thực hiện các giao diện hoặc lớp trừu tượng

0

Các lớp tĩnh và các thành viên lớp được sử dụng để tạo dữ liệu và chức năng có thể truy cập mà không cần tạo một thể hiện của lớp. Các thành viên lớp tĩnh có thể được sử dụng để phân tách dữ liệu và hành vi độc lập với bất kỳ nhận dạng đối tượng nào: dữ liệu và chức năng không thay đổi bất kể điều gì xảy ra với đối tượng. Các lớp tĩnh có thể được sử dụng khi không có dữ liệu hoặc hành vi nào trong lớp phụ thuộc vào nhận dạng đối tượng.

Một lớp có thể được khai báo là tĩnh, cho biết rằng lớp đó chỉ chứa các thành viên tĩnh. Không thể sử dụng từ khóa mới để tạo các cá thể của một lớp tĩnh. Các lớp tĩnh được nạp tự động bởi thời gian chạy ngôn ngữ chung của Khuôn khổ .NET (CLR) khi chương trình hoặc vùng tên chứa lớp được nạp.

Sử dụng lớp tĩnh để chứa các phương thức không được liên kết với một đối tượng cụ thể. Ví dụ, nó là một yêu cầu chung để tạo ra một tập hợp các phương thức không hành động trên dữ liệu cá thể và không được liên kết với một đối tượng cụ thể trong mã của bạn. Bạn có thể sử dụng một lớp tĩnh để giữ các phương thức đó.

Sau đây là các tính năng chính của một lớp tĩnh:

  1. Họ chỉ chứa các thành viên tĩnh.

  2. Chúng không thể được khởi tạo.

  3. Chúng được niêm phong.

  4. Chúng không thể chứa Trình tạo lập trình (Hướng dẫn lập trình C#).

Về cơ bản, tạo lớp tĩnh về cơ bản giống như tạo lớp chỉ chứa thành viên tĩnh và hàm tạo riêng. Một hàm tạo riêng tư ngăn không cho lớp được khởi tạo.

Lợi thế của việc sử dụng lớp tĩnh là trình biên dịch có thể kiểm tra để đảm bảo rằng không có thành viên cá thể nào được thêm vào vô tình. Trình biên dịch sẽ đảm bảo rằng các thể hiện của lớp này không thể được tạo ra.

Các lớp tĩnh được niêm phong và do đó không thể được kế thừa. Họ không thể kế thừa từ bất kỳ lớp nào ngoại trừ Object. Các lớp tĩnh không thể chứa một hàm tạo thể hiện; tuy nhiên, họ có thể có một hàm tạo tĩnh. Để biết thêm thông tin, hãy xem các Trình xây dựng tĩnh (Hướng dẫn lập trình C#).

-1

Khi chúng ta tạo một lớp tĩnh chỉ chứa các thành viên tĩnh và một hàm tạo riêng. Lý do duy nhất là hàm tạo tĩnh ngăn không cho lớp được khởi tạo để chúng ta không thể kế thừa một lớp tĩnh. các thành viên của lớp tĩnh bằng cách sử dụng tên lớp chính nó. Cố gắng kế thừa một lớp tĩnh không phải là một ý tưởng tốt.

1

Bạn có thể làm điều gì đó trông giống như kế thừa tĩnh.

Đây là mẹo:

public abstract class StaticBase<TSuccessor> 
    where TSuccessor : StaticBase<TSuccessor>, new() 
{ 
    protected static readonly TSuccessor Instance = new TSuccessor(); 
} 

Sau đó, bạn có thể làm điều này:

public class Base : StaticBase<Base> 
{ 
    public Base() 
    { 
    } 

    public void MethodA() 
    { 
    } 
} 

public class Inherited : Base 
{ 
    private Inherited() 
    { 
    } 

    public new static void MethodA() 
    { 
     Instance.MethodA(); 
    } 
} 

Lớp Inherited không phải là tĩnh bản thân, nhưng chúng tôi không cho phép để tạo ra nó. Nó thực sự đã thừa kế constructor tĩnh xây dựng Base, và tất cả các thuộc tính và phương thức của Base có sẵn như là tĩnh. Bây giờ điều duy nhất còn lại để thực hiện các trình bao bọc tĩnh cho mỗi phương thức và thuộc tính mà bạn cần để lộ ra với ngữ cảnh tĩnh của bạn.

Có những nhược điểm như nhu cầu tạo thủ công các phương pháp bao bọc tĩnh và từ khóa new. Nhưng cách tiếp cận này giúp hỗ trợ một cái gì đó thực sự tương tự như kế thừa tĩnh.

P.S. Chúng tôi đã sử dụng điều này để tạo các truy vấn được biên dịch, và điều này thực sự có thể được thay thế bằng ConcurrentDictionary, nhưng trường tĩnh chỉ đọc với an toàn luồng của nó là đủ tốt.

0

Một cách giải quyết bạn có thể làm là không sử dụng các lớp tĩnh nhưng ẩn hàm khởi tạo để các lớp thành viên tĩnh là thứ duy nhất có thể truy cập bên ngoài lớp. Kết quả là một "tĩnh" class thừa kế cơ bản:

public class TestClass<T> 
{ 
    protected TestClass() 
    { } 

    public static T Add(T x, T y) 
    { 
     return (dynamic)x + (dynamic)y; 
    } 
} 

public class TestClass : TestClass<double> 
{ 
    // Inherited classes will also need to have protected constructors to prevent people from creating instances of them. 
    protected TestClass() 
    { } 
} 

TestClass.Add(3.0, 4.0) 
TestClass<int>.Add(3, 4) 

// Creating a class instance is not allowed because the constructors are inaccessible. 
// new TestClass(); 
// new TestClass<int>(); 

Thật không may vì sự hạn chế ngôn ngữ "bởi thiết kế" chúng ta không thể làm:

public static class TestClass<T> 
{ 
    public static T Add(T x, T y) 
    { 
     return (dynamic)x + (dynamic)y; 
    } 
} 

public static class TestClass : TestClass<double> 
{ 
} 
Các vấn đề liên quan