2009-07-15 13 views
6

Tôi đang đối mặt với một vấn đề mà tôi không biết làm thế nào để giải quyết và hy vọng cộng đồng có thể giúp đỡ.Downcasting in C#

Tôi đang viết một ứng dụng quản lý đối tượng "Chì". (Đây là những khách hàng tiềm năng.) Một phần của chương trình của tôi sẽ nhập các khách hàng tiềm năng từ một tệp văn bản. Bây giờ, các tập tin văn bản có chứa rất nhiều tiềm năng dẫn, một số trong đó tôi sẽ muốn nhập khẩu và một số trong đó tôi sẽ không.

Để dễ lập trình (và sử dụng), tôi đang phân tích cú pháp tệp văn bản thành đối tượng < Chì > và sử dụng DataGridView để hiển thị khách hàng tiềm năng bằng cách đặt thuộc tính DataSource của DataGridView.

Điều tôi muốn làm là thêm một cột vào lưới, được gọi là "Nhập", với hộp kiểm mà người dùng có thể kiểm tra để cho biết có nên nhập từng đầu mối hay không.

Suy nghĩ đầu tiên của tôi là để lấy được một lớp từ chì:

 
public Class LeadWithImportCheckbox : Lead 
{ 
    bool bImport = false;

public bool Import { get { return bImport;} set { bImport = value;} } }

Tuy nhiên, động cơ phân tích cú pháp trả về một danh sách các đối tượng chì. Tôi không thể downcast một LeadWithImportCheckbox. Điều này không thành công:

 
LeadWithImportCheckbox newLead = (LeadWithImportCheckbox)LeadFromParsingEngine; 
Đây là dàn diễn viên không hợp lệ.

Các tùy chọn khác tôi thấy là để tạo ra một constructor cho LeadWithImportCheckbox:

 
public LeadWithImportCheckbox(Lead newlead) 
{ 
    base.Property1 = newlead.Property1; 
    base.Property2 = newlead.Property2; 
    .... 
    base.Property_n = newlead.Property_n; 
} 
Đây là vấn đề đối với hai lý do. Một, đối tượng Lead có vài chục thuộc tính và viết hàm khởi tạo này là một PITA.

Nhưng tệ hơn, nếu tôi thay đổi cấu trúc bên dưới của Lead, tôi cần phải nhớ quay lại và thay đổi hàm tạo này cho LeadWithImportCheckbox. Đây là mối nguy hiểm đối với việc bảo trì mã của tôi.

Có cách nào tốt hơn để hoàn thành mục tiêu của tôi không?

+0

Chỉ cần suy nghĩ ngẫu nhiên: có thể bạn có thể muốn theo dõi xem khách hàng tiềm năng đã "nhập" hay chưa (nhập bằng tay)? Nói cách khác, nó có thể khả thi để làm cho 'được nhập khẩu 'một tài sản cố định của' Chì'? – Adrien

+0

Tôi không nghĩ đó sẽ là thứ tôi muốn theo dõi. Tôi thấy những vấn đề này sẽ giải quyết nhưng nó thực sự không phải là một phần của những gì tôi thực sự muốn thực hiện. –

Trả lời

6

hay, để tránh những khía cạnh Pita, sử dụng phản ánh ... (thử này ...)

EDIT: sử dụng tài sản, chứ không phải Dòng như tôi đã ban đầu được viết ...

public class NewLead : Lead 
{ 
    public bool Insert; 
    public NewLead(Lead lead, bool insert) 
    { 
     Insert = insert; 
     foreach (PropertyInfo pi in typeof(Lead).GetProperties()) 
      GetType().GetProperty(pi.Name).SetValue 
       (this, pi.GetValue(lead,null), null); 
    } 
} 
+0

Tôi nghĩ rằng cuối cùng tôi thích cách tiếp cận phản chiếu. (Tôi chỉ thiếu hiểu biết.) Trong mã mẫu của bạn, tôi nhận được một mảng có độ dài bằng không từ typeof (Lead) .GetFields() –

+0

Tôi tin rằng đây là giải pháp mà tôi sẽ giải quyết. Cảm ơn, Charles! Mã mà tôi đã triển khai và dường như hoạt động là:

 public LeadWithImportCheckbox(Lead lead):base() { Type LeadType = typeof(Lead); PropertyInfo[] properties = LeadType.GetProperties(); foreach (PropertyInfo fi in properties) { object value = fi.GetValue(lead, null); fi.SetValue(this, value, null); } } 

+0

Giải pháp tuyệt vời. Tôi thích không phải cập nhật thủ công hàm tạo của tôi nếu lớp cơ sở thay đổi. – jocull

1

Những gì bạn muốn làm là hiển thị cột hộp kiểm trên lưới của bạn và không liên quan gì đến các đối tượng khách hàng tiềm năng của bạn. Bạn sử dụng các cột được đánh dấu (và có thể là Danh sách gốc) để xây dựng một bộ danh sách mới sẽ là danh sách nhập của bạn.

Sau đó, xử lý mọi thứ bạn muốn thực hiện với Danh sách mới được tạo.

Edit: Một điều cần phải cẩn thận trong khi làm việc với các danh sách là một thực tế tất cả các đối tượng lớp thực sự chỉ là một con trỏ tới lớp vì vậy nếu bạn làm việc với danh sách ban đầu và làm điều gì đó như:

List<Lead> Importable = new List<Lead>(); 

for(int i=0, i++, i<viewGrid.Count) 
    if(viewGrid[i].CheckedColumn.Checked) 
     Importable.Add(OriginalList[i]); 

Các đối tượng đó sẽ tồn tại trong cả hai danh sách và nếu bạn chỉnh sửa dữ liệu của một Khách hàng tiềm năng trên một trong hai danh sách, cả hai danh mục sẽ được thay đổi.

+0

Ban đầu tôi đã theo đuổi giải pháp này. Nó hoạt động phù hợp nếu tôi sử dụng Danh sách . Tuy nhiên, nếu tôi sử dụng một wrapper SortableBindingList , sau đó các dấu kiểm không ở lại với các chào khi DataGridView được sắp xếp. Kể từ khi tập tin văn bản sẽ có ~ 1.000 tiềm năng dẫn, phân loại là quan trọng. (Tôi xin lỗi vì không bao gồm điều này trong báo cáo vấn đề ban đầu; thật khó để biết có bao nhiêu thông tin là đủ nhưng không quá nhiều khi đơn giản hóa cho bài đăng.) –

4
public class LeadListItem 
{ 
    public Lead Lead { get; set; } 
    public bool ShouldImport { get; set; } 
} 

tức là không sao chép nội dung của đối tượng Lead, chỉ lưu trữ tham chiếu đến nội dung trong đối tượng LeadListItem mới, thêm thông tin bổ sung "bên ngoài" đối tượng ban đầu.

Nếu bạn muốn các thuộc tính của Chì xuất hiện trong lưới, gần như chắc chắn là một cách để thực hiện điều đó. Tại sao không hỏi câu hỏi đó, thay vì downvoting tôi cho bạn biết câu trả lời đúng cho câu hỏi này!

+0

Vấn đề với giải pháp này là các thành viên của Lead không hiển thị trong DataGridView. –

+0

@ The Demigeek Chắc chắn bạn có thể, nhưng đó là một câu hỏi khác. – exclsr

+0

GreenReign, đó là một câu hỏi khác nhau như thế nào? Câu hỏi ban đầu là về việc hiển thị một Chì với một hộp kiểm trong một DataGridView. Một giải pháp không bao gồm các thành viên của Lead trong DataGridView không phải là một giải pháp cho vấn đề. –

3

Một vài tùy chọn bạn có thể đã bỏ lỡ:

  • Bạn có thể cập nhật các đối tượng Chì bản thân để có một tài sản nhập khẩu (mà mặc định là false).
  • Bạn có thể có đối tượng "ImportLead" của bạn coi Chì là trọng tải (thậm chí làm cho nó chung, nếu bạn muốn), vì vậy bạn không cần hàm tạo lớn.
  • Tạo danh sách đối tượng khách hàng tiềm năng mới hoặc liệt kê chỉ chứa các đối tượng bạn muốn nhập ở địa điểm đầu tiên.
+0

Tôi sẽ đi với việc tạo ra một tài sản nhập khẩu trên chính lớp chì –

+0

Nhiều hơn hùng hồn đã nói về nỗ lực vụng về của tôi ở trên, nhưng khá nhiều những gì tôi đã cố gắng để nói. – Adrien

+4

Tôi sẽ không đồng ý với Stan. Không có lý do gì cho thuộc tính import trên Lead, nó hoàn toàn không liên quan đến Lead và là sự ô nhiễm đối tượng miền của bạn. Nó chỉ là một tạo phẩm giao diện người dùng và do đó nó không nên thay đổi các đối tượng miền của bạn. –

1

Bạn chỉ có thể downcast, nếu đối tượng bị downcast là thực sự đối tượng thuộc loại đó.

Một cách dễ dàng hơn để giải quyết vấn đề của bạn sẽ được để có một lớp DisplayLead, chẳng hạn như:

public class DisplayLead { 
     Lead lead; 
     bool bImport; 
    } 

đó cũng sẽ giúp bạn tách dữ liệu lưu trữ từ đại diện của họ trong một GUI.

+0

Nếu tôi làm điều này các thuộc tính của chì không hiển thị trong DataGridView. –

+1

Với thông tin bạn cung cấp để trả lời bài đăng của tôi, đây là giải pháp tối ưu. Những gì bạn cần làm là tắt tự động tạo cột và cần phải tự tạo cột và sử dụng biểu thức DataBinding để tiếp cận các mục chứa thực tế để bạn có thể đọc bên trong các đối tượng phức tạp. Nếu bạn tra cứu C# Databinding Eval hoặc tương tự trong google, bạn sẽ thấy cách thực hiện. –

0

Là giải pháp nhanh chóng và dơ bẩn, bạn có thể tạo đối tượng 'hộp kiểm' của mình làm đối tượng khác có chứa trường hợp Khách hàng tiềm năng.

public GridLead { 
    public bool Import { get; set; } 
    public Lead Lead { get; set; } 
} 

Bằng cách này bạn có thể dễ dàng thêm các thuộc tính 'lưới' khác vào đối tượng này, trong khi vẫn giữ nguyên tham chiếu đến chi tiết khách hàng mà không cần phải mã hóa tài sản.

+0

Nếu tôi làm điều này các thuộc tính của khách hàng tiềm năng không hiển thị trong DataGridView. –

0

Đề xuất bạn thử sửa đổi (nâng cấp) các đối tượng khách hàng tiềm năng đã nhập của bạn.

Hãy thử bắt đầu với các ví dụ here ...

1

tôi không thể nhìn xuống một cái gì đó nó không phải là. Nếu đối tượng được khởi tạo là Lead, thì nó không thể bị downcast đối với bất kỳ lớp dẫn xuất nào. Nếu nó được khởi tạo là LeadWithImportCheckbox và sau đó quay lại mã của bạn là Lead, thì bạn có thể downcast.

Protip: Kiểm tra loại thời gian chạy với toán tử is.

0

Nếu lớp khách hàng tiềm năng của bạn có trình tạo bản sao (ví dụ "Lead (Lead otherLead)"), LeadWithImportCheckbox sẽ kế thừa và bạn chỉ có thể gọi hàm tạo Lead cơ sở trong hàm tạo LeadWithImportCheckbox - do đó không cần LeadWithImportCheckbox các chi tiết của Lead.

1

Có rất nhiều cách để làm điều này, nhưng "đúng" cách bật ra vì những gì bạn nói, ở đây:

Để dễ dàng lập trình (và sử dụng), tôi phân tích các văn bản tập tin vào một đối tượng Danh sách Danh sách và sử dụng DataGridView để hiển thị khách hàng tiềm năng bằng cách đặt thuộc tính DataSource của DataGridView.

Những gì tôi muốn làm là thêm một cột để lưới điện, được gọi là "Import", với một hộp kiểm mà người dùng có thể kiểm tra chỉ ra hay không từng dẫn nên được nhập khẩu.

đối tượng Lead bạn đứng tốt trên riêng của mình, và bạn muốn đính kèm một số siêu dữ liệu để nó - bạn không muốn tạo ra một Lead phân loại (ví dụ: lớp LeadWithImportCheckbox).

Vì vậy, phương pháp tốt nhất trong trường hợp của bạn là để có một lớp học như vậy:

public class LeadInfo 
{ 
    private Lead lead; 
    private bool shouldImport; 

    public LeadInfo(Lead lead) 
    { 
     this.lead = lead; 
     this.ShouldImport = false; 
    } 

    public bool ShouldImport 
    { 
     get { return shouldImport; } 
     set { shouldImport = value; } 
    } 
} 

này sẽ có quy mô tốt khi bạn muốn thêm siêu dữ liệu thêm vào danh sách của bạn, như thế nào nếu bạn muốn gửi cho mình email nhắc nhở về họ mỗi tuần.

+0

Nhưng nếu tôi làm điều này, các thuộc tính Lead không hiển thị trên DataGridView. –

1

Tôi đã nhìn thấy các giải pháp chính xác được liệt kê rất nhiều lần tôi cảm thấy như một gót chân gửi nó một lần nữa, nhưng cách tốt nhất để tiếp cận này là viết một wrapper cho đối tượng Lead bao gồm các lá cờ nhập khẩu.

Nếu thuộc tính của đối tượng Lead không xuất hiện trong GridView vì bạn đang databinding đối tượng, sau đó viết passthrough thuộc tính phản ánh các thuộc tính Lead trên đối tượng wrapper.

Vấn đề là bạn muốn nội dung nào đó được hiển thị cho người dùng không phải là phần vốn có của mô hình dữ liệu. Câu trả lời là để bọc dữ liệu trước khi trình bày nó cho người dùng để bạn có thể kiểm soát những gì họ thấy mà không thay đổi mô hình cơ bản.

Nếu bạn lo ngại rằng đối tượng Lead sẽ thay đổi quá nhiều lần trong tương lai, những thay đổi đối với trình bao bọc sẽ rườm rà, bạn có thể xem xét việc tạo mã động dựa trên đối tượng Lead sẽ tự động tạo đối tượng bao bọc các trường giống với đối tượng Lead cùng với cờ nhập. Mặc dù thẳng thắn, đó là rất nhiều công việc hơn bạn có thể sẽ cần cho một cái gì đó đơn giản như thế này.

+0

Tôi không tin đây là giải pháp tối ưu. Nếu cấu trúc của đối tượng Lead bên dưới thay đổi, thì tôi cần quay lại và chỉnh sửa mã liên kết các thuộc tính của Lead với DataGridView. Thừa kế giải quyết vấn đề này. Không có vấn đề gì thay đổi được thực hiện để Chì, nếu LeadWithImportCheckbox kế thừa từ Chì, sau đó tôi không cần phải thực hiện bất kỳ thay đổi mã. Và 'LeadThatIMightWantToImport' Is-A 'Lead'. Thừa kế là thích hợp. Tôi tin rằng bằng cách sử dụng Phản chiếu, như Charles đã đề xuất, sẽ là cách đúng để tạo LeadWithImportCheckbox từ một khách hàng tiềm năng. –

+0

Cách tiếp cận phản chiếu sẽ làm giảm bớt vấn đề 'thay đổi đối tượng dẫn tiềm ẩn', nhưng như tôi đã nói, nếu đối tượng Lead của bạn thay đổi thường xuyên, có những vấn đề sâu sắc hơn thực hành mã hóa của bạn. Dựa trên phản ứng của bạn đối với giải pháp của Charles, tôi không chắc chắn sự phản chiếu sẽ là cách phù hợp để đi cho bạn. Cũng lưu ý rằng việc sử dụng sự phản chiếu theo cách đó sẽ áp đặt một hình phạt nghiêm trọng về hiệu suất (có thể là một vấn đề nếu bạn bắt đầu có hàng nghìn khách hàng tiềm năng). – genki