2014-12-15 17 views
9

Tôi đã một lớp định nghĩa là:Child constructor tĩnh không được gọi là khi thành viên cơ sở truy cập

public class DatabaseEntity<T> where T : DatabaseEntity<T> { 
    public static string Query { get; protected set; } 
    public static IList<T> Load() { 
     return Database.Get(Query); 
    } 
} 

public class Node : DatabaseEntity<Node> { 
    static Node() { 
     Node.Query = @"SELECT Id FROM Node"; 
    } 
} 

Khi tôi chạy Node.Load() từ một codebehind (Window.xaml.cs) constructor tĩnh của Node bao giờ cháy; hoặc ít nhất không đạt điểm ngắt và không đặt Node.Query thành bất kỳ thứ gì khác ngoài null.

Có lý do nào khiến điều này có thể xảy ra không?

Giải pháp

Kiểm tra các câu trả lời dưới đây cho một số giải pháp. Đối với trường hợp của tôi, tôi quyết định chỉ cần đặt biến số Query là công khai và đặt tất cả các phiên bản Query ở một nơi. (Không phải lý tưởng, nhưng nó hoạt động.)

+0

Có phải nó đang bị hủy đăng ký qua DataContractSerializer không? Khi tôi nhớ lại DCS không kích hoạt các nhà xây dựng. – PhillipH

+0

Tại sao không chỉ đơn giản là thực hiện truy vấn 'private const string Query = [query];'? – atlaste

+0

Nó không phải là. Tuy nhiên, 'Load()' thực sự là một hàm tĩnh kế thừa. Điều đó có tạo nên sự khác biệt không? –

Trả lời

4

Sự cố nằm trong các giả định của bạn về thời điểm một hàm tạo tĩnh được gọi. documentation, không rõ ràng nhất, nêu rõ rằng

Nó được gọi tự động trước khi phiên bản đầu tiên được tạo hoặc bất kỳ thành viên tĩnh nào được tham chiếu.

Bạn có thể giả định rằng nếu bạn gọi

Node.Load(); 

mà bạn đang gọi một phương thức tĩnh trên lớp Node, nhưng trong thực tế, bạn đang gọi điện thoại nó trên lớp cơ sở, vì đó là nơi nó được thực thi.

Vì vậy, để khắc phục điều này, bạn có hai lựa chọn. Trước tiên, bạn có thể kích hoạt các constructor tĩnh một cách rõ ràng bằng cách tạo ra một thể hiện mới của lớp Node trước khi gọi Load()

var foo = new Node(); // static ctor triggered 
Node.Load(); 

hoặc tạo ra một thành viên ảo được bảo vệ mà các lớp cơ sở có thể gọi để có được giá trị truy vấn (có thể 't sử dụng trừu tượng ở đây, thật không may)

public class DatabaseEntity<T> where T : Derp { 
    protected abstract string Query { get; } 
    public static IList<T> Load() {   
     return Database.Get(new DatabaseEntity<T>().Query); 
    } 
} 

Cả hai đều bị hacky. Tốt hơn để phân phối với các thống kê hoàn toàn và đi với các phương pháp dụ. Thống kê nên được sử dụng một cách tiết kiệm, vì chúng dẫn đến khớp nối chặt chẽ và các cơn đau đầu thiết kế khác như thế này.

4

Có, các hàm tạo tĩnh sẽ không được gọi cho đến khi các thành viên của lớp được truy cập lần đầu hoặc được tạo lần đầu tiên.

Trong trường hợp của bạn, bạn đang truy cập DatabaseEntity<T>.Load, vì vậy hàm tạo tĩnh của DatabaseEntity<T> sẽ được gọi không phải là lớp dẫn xuất của nó.

Mặc dù bạn gọi Node.Load nó được ánh xạ tới DatabaseEntity<Node> lúc biên dịch. Vì vậy, về mặt kỹ thuật, bạn không truy cập vào lớp học Node.

+0

Điều này là chính xác, bạn có thể chứng minh hành vi này bằng cách tạo một thể hiện của lớp nút trước khi gọi Node.Load() và nó sẽ hoạt động. Nếu không có trường hợp nào được thực hiện trước khi cuộc gọi đó sẽ không được thực hiện. –

+0

Đây là một lời giải thích tuyệt vời. Bạn có thể đưa ra bất kỳ gợi ý hay lời khuyên nào về cách giải quyết vấn đề này không? (Bên cạnh đó, 'Node n = new Node();') –

+2

@CharlesW Tôi nghi ngờ đây là một vấn đề thiết kế. Nếu không biết nhiều hơn về những gì bạn đang cố gắng đạt được, tôi không thể giúp được điều này. Cách giải quyết rõ ràng sẽ là thêm phương thức 'Node.Load' với công cụ sửa đổi mới mà chỉ đơn giản gọi thực hiện lớp cơ sở, nhưng. Tôi không khuyên bạn nên. –

Các vấn đề liên quan