2009-04-02 16 views
20

Tôi chắc chắn nhớ đã thấy ở đâu đó một ví dụ về làm như vậy bằng cách sử dụng sự phản chiếu hoặc một cái gì đó. Đó là một cái gì đó đã làm với SqlParameterCollection mà không thể tạo ra bởi một người dùng (nếu tôi không nhầm lẫn). Thật không may không thể tìm thấy nó nữa.Làm thế nào để khởi tạo một đối tượng với một hàm tạo riêng trong C#?

Mọi người có thể chia sẻ mẹo này ở đây không? Không phải là tôi coi nó là một cách tiếp cận hợp lệ trong phát triển, tôi chỉ rất quan tâm đến khả năng làm điều này.

Trả lời

33
// the types of the constructor parameters, in order 
// use an empty Type[] array if the constructor takes no parameters 
Type[] paramTypes = new Type[] { typeof(string), typeof(int) }; 

// the values of the constructor parameters, in order 
// use an empty object[] array if the constructor takes no parameters 
object[] paramValues = new object[] { "test", 42 }; 

TheTypeYouWantToInstantiate instance = 
    Construct<TheTypeYouWantToInstantiate>(paramTypes, paramValues); 

// ... 

public static T Construct<T>(Type[] paramTypes, object[] paramValues) 
{ 
    Type t = typeof(T); 

    ConstructorInfo ci = t.GetConstructor(
     BindingFlags.Instance | BindingFlags.NonPublic, 
     null, paramTypes, null); 

    return (T)ci.Invoke(paramValues); 
} 
+0

Một cải tiến nhỏ cho phương pháp tĩnh tiện dụng này là xây dựng các mảng kiểu tham số động – nrjohnstone

+1

@nrjohnstone Nếu bạn có nghĩa là GetType trên mỗi phần tử mảng nó không thể trong trường hợp chung vì null. Ngoài ra nếu bạn cố gắng suy ra các loại trong trường hợp này, bạn sẽ kết thúc việc thực hiện một độ phân giải quá tải hoàn chỉnh theo ngôn ngữ của sự lựa chọn, mà không phải là tầm thường trong trường hợp của C#. –

2

Nếu lớp học không phải là của bạn, có vẻ như API đã được cố tình viết để ngăn chặn điều này, có nghĩa là có thể cách tiếp cận của bạn không phải là ý tưởng của các nhà văn API. Hãy xem các tài liệu và xem liệu có cách tiếp cận được khuyến nghị để sử dụng lớp này hay không.

Nếu bạn do có quyền kiểm soát lớp và muốn triển khai mẫu này, khi đó nó thường được triển khai thông qua phương pháp tĩnh trên lớp. Đây cũng là một khái niệm quan trọng tạo nên mẫu Singleton.

Ví dụ:

public PrivateCtorClass 
{ 
    private PrivateCtorClass() 
    { 
    } 

    public static PrivateCtorClass Create() 
    { 
     return new PrivateCtorClass(); 
    } 
} 

public SomeOtherClass 
{ 
    public void SomeMethod() 
    { 
     var privateCtorClass = PrivateCtorClass.Create(); 
    } 
} 

Những thứ SqlCommandParameter là một ví dụ điển hình. Họ mong đợi bạn để tạo ra các thông số bằng cách gọi những thứ như thế này:

var command = IDbConnnection.CreateCommand(...); 
command.Parameters.Add(command.CreateParameter(...)); 

Ví dụ của tôi không phải là mã tuyệt vời vì nó không chứng minh thiết lập các thuộc tham số lệnh hoặc tái sử dụng các thông số/lệnh, nhưng bạn sẽ có được ý tưởng.

+0

Tôi thực sự có nghĩa là nếu tác giả nếu lớp (không phải tôi) làm tất cả các hàm tạo riêng hoặc nội bộ và tôi không thể sửa đổi lớp, nhưng thực sự, thực sự muốn tạo một thể hiện của nó, sau đó làm thế nào tôi có thể nhận được sự bảo vệ này và tạo một cá thể? – User

+0

Ahh tôi hiểu ý bạn là gì. Vâng, có vẻ như API đã được cố ý viết để ngăn chặn điều này, có nghĩa là có thể cách tiếp cận của bạn không phải là những gì mà các nhà văn API dự định. Hãy xem các tài liệu và xem liệu có cách tiếp cận được khuyến nghị để sử dụng lớp này hay không. –

47

Bạn có thể sử dụng một trong những định nghĩa chồng cho Activator.CreateInstance để làm điều này: Activator.CreateInstance(Type type, bool nonPublic)

Sử dụng true cho lập luận nonPublic. Bởi vì true khớp với một hàm tạo mặc định công khai hoặc không công khai; và false chỉ khớp với hàm tạo mặc định công khai.

Ví dụ:

class Program 
    { 
     public static void Main(string[] args) 
     { 
      Type type=typeof(Foo); 
      Foo f=(Foo)Activator.CreateInstance(type,true); 
     }  
    } 

    class Foo 
    { 
     private Foo() 
     { 
     } 
    } 
+1

Trong Silverlight, nó khớp với 'CreateInstance (kiểu kiểu, params object [] args);' mà lốp để gọi ** ctor ** public với đối số ** bool **. http://msdn.microsoft.com/en-us/library/system.activator.createinstance%28v=vs.95%29.aspx –

+2

Đó là ok nếu bạn gọi constructor parameterless. Nếu bạn muốn gọi một hàm tạo riêng với các tham số, hãy thử: Trình kích hoạt Foo f = (Foo).CreateInstance (typeof (Foo), BindingFlags.Instance | BindingFlags.NonPublic, null, đối tượng mới [] {"Param1"}, null, null); ' –

0

Nó cũng sẽ giúp đỡ nếu Type của bạn là private hoặc internal:

public static object CreatePrivateClassInstance(string typeName, object[] parameters) 
    { 
     Type type = AppDomain.CurrentDomain.GetAssemblies(). 
       SelectMany(assembly => assembly.GetTypes()).FirstOrDefault(t => t.Name == typeName); 
     return type.GetConstructors()[0].Invoke(parameters); 
    }