2009-04-27 21 views
5

Tôi muốn có một Singleton sẽ được tự động khởi tạo khi bắt đầu chương trình.Làm thế nào để: Tự động khởi tạo Singleton trong C#

Điều tôi ngụ ý bằng "tự động khởi tạo" là mã trong Singleton sẽ tự khởi động ngay khi bắt đầu chương trình mà không có bất kỳ cuộc gọi hoặc khai báo nào bằng mã khác.

Vì vậy, tôi muốn một cái gì đó như sau để nhanh chóng và viết ra "MySingleton instantiated" trên bắt đầu chương trình (không có mã chính làm bất cứ điều gì) ...

static class MySingleton 
{ 
    private static MySingleton self = new MySingleton(); 

    protected MySingleton() 
    { 
     System.Console.WriteLine("MySingleton Instantiated"); 
    } 
} 

trừ này không làm việc kể từ C# sẽ chỉ khởi tạo các thành viên tĩnh của một lớp khi cần, tức là khi chúng được truy cập/etc.

Vì vậy, tôi phải làm gì? điều này có thể được thực hiện?

Tôi đã không làm điều này cá nhân với C + + (đã không được sử dụng C + + trong một thời gian) nhưng tôi khá chắc chắn nó có thể được thực hiện trong C + + nhưng không chắc chắn về C#.

Mọi trợ giúp đều được đánh giá cao. Cảm ơn.


Những gì tôi đang thực sự muốn làm với điều này là ... Sẽ có rất nhiều các lớp singleton (và nhiều hơn nữa có thể được thêm vào như thời gian đi về), tất cả trong số đó sẽ kế thừa từ một chung (abstract) lớp cha (aka. PClass).

Các PClass sẽ có một thành viên tĩnh mà là một tập hợp các PClasses ... và một constructor để thêm chính nó vào bộ sưu tập ...

Sau đó, về mặt lý thuyết tất cả các độc thân sẽ Automagically được bổ sung vào bộ sưu tập (kể từ khi chúng được khởi tạo, hàm tạo PClass cơ sở được gọi và thêm đối tượng mới vào bộ sưu tập) ... thì bộ sưu tập có thể được sử dụng mà không biết bất kỳ điều gì về các lớp con (singleton) đã được triển khai và các lớp con mới (singleton) có thể được thêm vào bất kỳ lúc nào mà không phải thay đổi bất kỳ mã nào khác.

Thật không may là tôi không thể khiến trẻ em (người độc thân) tự khởi tạo bản thân ... làm hỏng kế hoạch nhỏ của tôi, dẫn đến bài đăng này.

Hy vọng tôi đã giải thích rõ điều đó.


PS. Có, tôi nhận ra có những cảm xúc xấu về Singletons và việc sử dụng chúng ... nhưng đôi khi chúng hữu ích, và ngay cả khi Satan tự làm Singletons tôi vẫn muốn biết liệu vấn đề của tôi có thể đạt được trong C# hay không. Cảm ơn tất cả các bạn.

+2

Satan là Singleton. –

Trả lời

6

Phương pháp IoC đề cập bởi Chris có lẽ là tốt nhất, nhưng thất bại đó "tốt nhất" giải pháp tôi có thể nghĩ đến là để làm một cái gì đó sôi nổi với sự phản ánh và các thuộc tính dọc theo dòng:

public class InitOnLoad : Attribute 
{ 
    public static void Initialise() 
    { 
     // get a list of types which are marked with the InitOnLoad attribute 
     var types = 
      from t in AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) 
      where t.GetCustomAttributes(typeof(InitOnLoad), false).Count() > 0 
      select t; 

     // process each type to force initialise it 
     foreach (var type in types) 
     { 
      // try to find a static field which is of the same type as the declaring class 
      var field = type.GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).Where(f => f.FieldType == type).FirstOrDefault(); 
      // evaluate the static field if found 
      if (field != null) field.GetValue(null); 
     } 
    } 
} 

[InitOnLoad] 
public class Foo 
{ 
    public static Foo x = new Foo(); 

    private Foo() 
    { 
     Console.WriteLine("Foo is automatically initialised"); 
    } 
} 

public class Bar 
{ 
    public static Bar x = new Bar(); 

    private Bar() 
    { 
     Console.WriteLine("Bar is only initialised as required"); 
    } 
} 

Với một hãy gọi đến InitOnLoad.Initialise() được thêm vào phương thức chính của bạn.

Bạn có thể loại bỏ thuộc tính, nhưng điều này có thể gây ra các loại không cần thiết được khởi tạo và không cần thiết tiêu thụ bộ nhớ (chẳng hạn như Bar trong mã trên). Nó cũng đáng chú ý rằng điều này sẽ không xử lý các loại chứa trong bất kỳ hội đồng nào được nạp động, trừ khi bạn thực hiện một cuộc gọi khác để khởi tạo, nhưng dựa trên câu hỏi của bạn ("tự động khởi tạo khi bắt đầu chương trình") âm thanh như một vấn đề cho bạn.

+0

Sau khi cập nhật câu hỏi, bạn có thể thay thế thuộc tính bằng kiểm tra loại tổ tiên. Mã Initialise sau đó có thể được chuyển đến một constructor tĩnh trong lớp cơ sở, ngay sau khi lớp cơ sở được tham chiếu trong ứng dụng, nó sẽ làm cho tất cả các tổ tiên được nạp là tốt. – Richard

+0

Tôi đã nghe nói về Reflection nhưng chưa sử dụng nó ... đã tự hỏi nếu nó có thể được sử dụng cho một điều như vậy ... Tôi nghĩ rằng điều này nghe có vẻ như giải pháp tốt nhất, và nhận xét tiếp theo của bạn mô tả về cơ bản chính xác những gì tôi muốn làm. Với Reflection tôi nghĩ rằng điều này có thể đạt được. Cảm ơn. – devlop

5

Trong khi các mô-đun .NET có thể lý thuyết (IIRC) phản ứng với tải mô-đun vv, điều này không có sẵn thông qua C#. Trong một số khung công tác (như ASP.NET) có các móc bạn có thể sử dụng thông qua cấu hình, chẳng hạn như hack thông qua trình xử lý hoặc qua global.asax.cs - tuy nhiên, đối với ứng dụng C# thông thường (bảng điều khiển, winform, v.v), bạn sẽ phải kích hoạt nó bằng tay. Ví dụ: một hàm tạo tĩnh trên lớp lưu trữ điểm nhập Main của bạn sẽ được gọi.

Vì vậy: trường hợp sử dụng ở đây là gì? Khi nào thì cách tiếp cận tải lười sẽ không sao?

+0

Tôi đã chỉnh sửa bài đăng để thêm trường hợp sử dụng của mình. – devlop

2

Tôi nghi ngờ điều này là có thể mà không làm bất cứ điều gì từ Main. Ngay cả việc thêm static MySingleton() {} vào lớp học cũng không đảm bảo sự khởi tạo của nó nếu bạn không sử dụng nó.

0

Tôi không tin rằng điều bạn muốn có thể thực hiện trong .Net framework. Về cơ bản, bạn muốn một cái gì đó giống như thời gian chạy để thông báo các loại hoặc gọi một số phương pháp tĩnh được xác định trước trên chúng mà ứng dụng được tải và chạy hoặc cung cấp một cơ chế như thế. Hãy suy nghĩ về chi phí. Điều duy nhất mà thời gian chạy đảm bảo là tự động gọi phương thức nhập chính cho chương trình của bạn. Đó là nó. Ở đó bạn có thể thiết lập mọi thứ và khởi tạo chúng nhưng không có gì tự động về nó. Bạn vẫn đang rõ ràng gắn kết mọi thứ và thực hiện cuộc gọi phương thức.

+0

Tôi bắt đầu nghĩ rằng nó không thể ... nhưng nó phải được! Tôi khá chắc chắn nó có thể được thực hiện trong C + +, mặc dù tôi đã không thực sự làm nó ... và ya rõ ràng C# không phải là C + + nhưng nó khó chịu mà loại điều không thể được thực hiện trong C#. – devlop

+0

Tôi nghi ngờ nó thậm chí có thể trong C + + hoặc. Tôi nhớ có một số callback tiêu chuẩn trong mỗi dll mà Windows gọi mỗi khi một dll được nạp, unloaded, thread đính kèm, vv như Mark Gravell đã đề cập nhưng ngay cả sau đó bạn sẽ cần phải khởi tạo một cách rõ ràng các singletons của bạn. Ngoài ra, trong .NET, bạn không có quyền truy cập vào các cuộc gọi lại đó. Có thể là một khả năng là có bootstrapper của riêng bạn để lưu trữ .NET runtime. Sau đó, bạn có thể sử dụng CLR lưu trữ apis hoặc một số thủ thuật để làm những gì bạn muốn nhưng tại sao? Tôi nghĩ bạn nên suy nghĩ lại về thiết kế của mình và sự cần thiết của yêu cầu này. –

1

Về cơ bản, bạn đang yêu cầu .NET framework gọi một số chức năng bất cứ khi nào nó tải một assembly. Chức năng đó sẽ là đăng ký phiên bản PClass.

Không có DllMain trong C#. Bạn có thể mô phỏng điều này bằng cách có một thuộc tính mức assembly và một số mã trong phương thức chính của bạn, mà lắng nghe bất cứ khi nào một assembly được nạp. Thuộc tính có thể có điểm nhập "DllMain" được chỉ định hoặc trỏ đến các lớp kế thừa PCIass của bạn.

1

Tiếp theo, cho phép tôi chỉ ra rằng đây không thực sự là mẫu đơn như nó thường được triển khai. Đây là cách triển khai bình thường (được đơn giản hóa) trong C# (không bao gồm các nội dung như an toàn luồng cho ngắn gọn):

public sealed class Singleton 
{ 
static readonly Singleton _instance = new Singleton(); 

// private ctor 
Singleton() {} 

public static Singleton Instance 
{ 
    get { return _instance; } 
} 
} 

Dòng mã này thậm chí không được biên dịch!

protected MySingleton() 

Đã nói tất cả điều đó, tôi đồng ý với người đã hỏi về trường hợp sử dụng của bạn. Đó là điều tốt để biết. =)

+0

Ya, thông thường mọi người có một người truy cập ví dụ như bạn chứng minh, nhưng vấn đề của tôi là tôi không muốn bất kỳ mã nào khác truy cập trực tiếp vào singleton ... Tôi chỉ muốn nó tự khởi tạo ... nên không cần một người truy cập. Ngoài ra, tôi đã chỉnh sửa câu hỏi của mình để thêm giải thích ca sử dụng. – devlop

+0

BTW, có gì sai với một nhà xây dựng được bảo vệ. Nó biên dịch tốt. – devlop

+0

Bạn sẽ nhận được một lỗi trình biên dịch ("Các lớp tĩnh không thể có các hàm tạo mẫu") khi bạn thêm một ctor (của bất kỳ cấp truy cập nào) vào một lớp tĩnh. – Garrett

4

Dựa trên những gì bạn đang cố gắng làm, tôi sẽ bỏ ý tưởng về Singleton thực sự và sử dụng thư viện IoC thay thế.

Kiểm tra StructureMap, Castle Windsor, Ninject và/hoặc Autofac.

Điều này sẽ cho phép bạn tạo lớp như một singleton, thông qua thư viện IoC, có bao nhiêu tùy thích, nhưng nó chỉ là một lớp cũ đơn giản.

Độc thân có vấn đề ở chỗ họ thực sự làm rối loạn khả năng kiểm tra (thông qua thử nghiệm đơn vị) của ứng dụng của bạn.

Chỉ cần thực hiện tìm kiếm trên google về "Singleton coi là có hại" và bạn sẽ thấy nhiều tài liệu tham khảo hơn.

Ngoài ra, bạn cũng có thể sử dụng mẫu nhà máy sản xuất Lớp/Phương pháp đơn giản.

+0

Tôi không quen thuộc với các thư viện IoC ... và cho các nhu cầu của tôi có vẻ phức tạp hơn tôi muốn? Hiện tại là một giải pháp về cơ bản tôi đang làm một phương pháp nhà máy. Nhưng nó không phải là tốt đẹp. – devlop

+0

Một IoC không phải là tất cả những gì phức tạp, thực sự. Tiền thưởng thêm là một IoC cũng có thể giúp phần còn lại của ứng dụng của bạn cũng vì tự động Dependency Injection. Ngay cả khi bạn không sử dụng nó cho vấn đề này, IoC đáng xem xét. –

0

"The PClass sẽ có một thành viên tĩnh mà là một tập hợp các PClasses ..."

Âm thanh như một cái gì đó mà có thể được thực hiện dễ dàng với Reflection. Bạn không cần mã chạy khi khởi động; bạn có thể chỉ cần xây dựng danh sách các lớp này bất cứ khi nào bạn cần. Dưới đây là một ví dụ sẽ tải danh sách một lần khi bạn lần đầu tiên đọc tài sản Instances, sẽ tìm trong tất cả các assembly được nạp vào thời điểm đó (bạn có thể đơn giản hóa điều này một chút nếu bạn chỉ muốn tìm trong cùng một assembly như PClass, nhưng câu hỏi của bạn không chỉ rõ các assembly bạn muốn xem), và sẽ thêm một cá thể của bất kỳ hậu duệ PClass nào (nhưng không phải bản thân PClass) vào danh sách. Mỗi hậu duệ PClass sẽ cần một hàm tạo không có tham số, nhưng bạn sẽ không cần bất kỳ các hàm tạo lớp nào.

public class PClass 
{ 
    private static List<PClass> m_instances; 
    public static IList<PClass> Instances 
    { 
     get 
     { 
      if (m_instances == null) 
       m_instances = LoadInstanceList(); 
      return m_instances; 
     } 
    } 
    private static List<PClass> LoadInstanceList() 
    { 
     foreach (var assembly in AppDomain.GetAssemblies()) 
     { 
      foreach (var type in assembly.GetTypes()) 
      { 
       if (type.IsAssignableTo(typeof(PClass)) && type != typeof(PClass)) 
        m_instances.Add(Activator.CreateInstance(type)); 
      } 
     } 
    } 
} 
+0

Cảm ơn, Richard đã ở trong đó đầu tiên với các công cụ Reflection ... do đó, cho Check với anh ta. Nhưng cảm ơn cho câu trả lời anyway. Bạn không thể thực hiện nhiều lần kiểm tra ở đây? – devlop

+0

Activator.CreateInstance yêu cầu (theo mặc định ít nhất) một hàm khởi tạo công khai mặc định. Ví dụ singleton đưa ra không cung cấp một, đây là lý do tại sao tôi đã sử dụng sự phản chiếu để tìm một trường có cùng loại với lớp khai báo và sau đó lấy giá trị của trường đó. – Richard

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