2011-01-28 25 views
7

Ok, tôi đã đập thình thịch ý tưởng này cả ngày nay, và tôi đã đạt đến phần mà tôi thừa nhận là tôi không biết. Có thể những gì tôi đang làm chỉ là ngu ngốc và có một cách tốt hơn, nhưng đây là nơi mà suy nghĩ của tôi đã mang lại cho tôi.Làm cách nào bạn có thể truyền sang một loại bằng cách sử dụng tên loại làm chuỗi?

Tôi đang cố gắng sử dụng một phương pháp tổng quát để tải các hình thức trong WinForms:

protected void LoadForm<T>(ref T formToShow, bool autoLoaded) where T : FormWithWorker, new() 
{ 
    // Do some stuff 
} 

Các hình thức được nạp bởi một ToolStripMenuItem (hoặc thông qua việc lựa chọn các mục hoặc bằng cách sử dụng menu Open Windows). Chúng được tải xuống, do đó, có các trường cho các biểu mẫu bên trong cha mẹ MDI, nhưng chúng là rỗng cho đến khi chúng cần thiết. Tôi có một phương pháp phổ biến được sử dụng cho ToolStripMenuItem_Click xử lý tất cả các nhấp chuột vào mục menu. Phương thức này không có cách thức thực sự để biết biểu mẫu nào đang được gọi cho ngoại trừ tên của ToolStripMenuItem khớp với một mẫu được chọn cho các tên lớp biểu mẫu mà chúng tương ứng. Vì vậy, bằng cách sử dụng tên của ToolStripMenuItem, tôi có thể thần thánh tên của loại biểu mẫu đang được yêu cầu và tên của trường riêng được cấp phát để lưu trữ tham chiếu cho biểu mẫu đó.

Sử dụng điều đó, tôi có thể sử dụng câu lệnh chuyển đổi/ký kết hợp đồng với các kiểu được mã hóa cứng và chuỗi kết hợp với phương thức gọi với tập hợp cụ thể (không mong muốn) hoặc tôi có thể sử dụng Reflection để lấy trường và tạo cá thể của loại. Vấn đề với tôi là, System.Activator.CreateInstance cung cấp một ObjectHandler mà không thể được đúc thành các loại mà tôi cần. Dưới đây là một đoạn của những gì tôi có cho đến nay:

string formName = "_form" + ((ToolStripMenuItem)sender).Name.Replace("ToolStripMenuItem", ""); 
string formType = formName.Substring(1); 

FieldInfo fi = this.GetType().GetField(formName, BindingFlags.NonPublic | BindingFlags.Instance); 

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this); 
if (formToLoad == null) 
{ 
    formToLoad = (????)System.Activator.CreateInstance("MyAssemblyName", formType); 
} 

this.LoadForm(ref formToLoad, false); 
fi.SetValue(this, formToLoad); 

tôi biết tên chuỗi của các loại mà đi ở cho (????) nhưng tại thời gian biên dịch Tôi không biết loại vì nó thay đổi . Tôi đã thử một loạt các cách để có được dàn diễn viên/instantiation này để làm việc, nhưng không ai có thể thành công. Tôi rất muốn biết nếu nó có thể thực hiện một diễn viên như vậy biết loại chỉ như một chuỗi. Tôi đã thử sử dụng Type.GetType(string, string) để thực hiện diễn viên, nhưng trình biên dịch không thích nó. Nếu ai đó có ý tưởng khác về cách tải các biểu mẫu động bởi vì tôi chỉ làm điều đó một cách ngu ngốc, hãy cho tôi biết về nó.

+0

tạo đối tượng formToLoad và truyền lên FormWithWorker tại điểm LoadForm và SetValue? – asawyer

Trả lời

5

Bạn muốn được tốt hơn off với other overload mà phải mất một Type và sử dụng ví dụ Type.GetType(string).

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this); 
if (formToLoad == null) 
{ 
    formToLoad = 
     (FormWithWorker)System.Activator.CreateInstance(Type.GetType("MyNamespace.MyFormType")); 
} 
+0

Tôi nhớ tại một thời điểm bài viết này đã đề cập cụ thể việc sử dụng phương thức '.UnWrap()' trên 'System.Activator.CreateInstance'. Nó không nói nó bây giờ, nhưng nó nên vì giải pháp cho vấn đề của tôi là khai báo trường riêng với kiểu của lớp cơ sở và sau đó gọi 'CreateInstance (" namespace "," type "). Unwrap()'. Điều này tạo ra loại thích hợp và cho phép phương thức này khởi tạo đúng cách. –

12

Sự cố này thường được giải quyết bằng cách truyền tới một lớp cơ sở chung hoặc giao diện của tất cả các loại tiềm năng.

Trong C# 4, bạn cũng có thể gán cho biến số dynamic để giữ giá trị trả lại và gọi các phương thức tùy ý trên đó. Các phương pháp sẽ bị ràng buộc trễ. Tuy nhiên, tôi thích gắn bó với giải pháp cũ bất cứ khi nào có thể.

+0

Bỏ phiếu. Tôi đã làm một cái gì đó rất giống nhau bằng cách sử dụng một giao diện và nó hoạt động độc đáo. – Chuck

+0

Tôi đã thử phương pháp này, nhưng vấn đề tôi gặp phải là kiểu động mà nó đã gán là lớp cơ sở chứ không phải lớp thực, vì vậy khi gọi phương thức SetValue, nó đã ném một ngoại lệ kiểu đúc. –

+0

@Joel như câu trả lời khác chỉ ra, bạn nên sử dụng quá tải mà trả về đối tượng chính nó. Rõ ràng, quá tải bạn đang sử dụng rất hữu ích cho việc kích hoạt các đối tượng từ xa .NET. –

2

Theo những gì bạn có, FormWithWorker phải (ít nhất) là lớp cơ sở của các loại bạn đang instantiating, vì vậy bạn có thể làm điều này:

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this); 
if (formToLoad == null) 
{ 
    formToLoad = (FormWithWorker)System.Activator.CreateInstance("MyAssemblyName", formType); 
} 
+0

Tôi đã thử điều này, nhưng tôi đã nhận được lỗi truyền do phương thức 'System.Activator.CreateInstance' trả về một ObjectHandler. Tôi chỉ đọc ý tưởng gọi là "Unwrap()" của @Andras Vass và kết hợp 2 là những gì tôi sẽ cố gắng tiếp theo. –

0

Trong khi một giao diện chung là một cách để tiếp cận này vấn đề, giao diện không thực tế đối với tất cả các scenerio. Quyết định trên là một trong những đi với một mô hình nhà máy (chuyển đổi tuyên bố - lựa chọn lớp bê tông) hoặc sử dụng sự phản ánh. Có một bài đăng ngăn xếp giải quyết vấn đề này. Tôi tin rằng bạn có thể trực tiếp áp dụng điều này để vấn đề của bạn:

Method Factory - case vs. reflection

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