2010-11-03 29 views
5

Tôi có một ListView ở chế độ ảo và dữ liệu cơ bản đang được lưu trữ trong một List<MyRowObject>. Mỗi cột của ListView tương ứng với thuộc tính chuỗi công khai là MyRowObject. Các cột của ListView của tôi có thể được định cấu hình trong suốt thời gian chạy, vì vậy bất kỳ cột nào trong số chúng có thể bị vô hiệu hóa và chúng có thể được sắp xếp lại. Để trở về một ListViewItem cho sự kiện RetrieveVirtualItem, tôi có một phương pháp tương tự như:Có cách nào tốt đẹp để tránh sử dụng sự phản chiếu để điền vào ListView ảo của tôi không?

class MyRowObject 
{ 
    public string[] GetItems(List<PropertyInfo> properties) 
    { 
     string[] arr = new string[properties.Count]; 
     foreach(PropertyInfo property in properties) 
     { 
      arr[i] = (string)property.GetValue(this,null); 
     } 
     return arr; 
    } 
} 

Việc xử lý sự kiện cho RetrieveVirtualItem trông tương tự như:

private void listView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) 
{ 
    e.Item = new ListViewItem(_virtualList[e.ItemIndex].GetItems(_currentColumns)); 
} 

Có lẽ không đáng ngạc nhiên, điểm chuẩn cho thấy phương pháp này là đáng kể chậm hơn so với triển khai đã truy cập các thuộc tính trực tiếp theo thứ tự được mã hóa cứng và sự chậm lại chỉ đủ quan trọng để tôi muốn tìm một giải pháp tốt hơn.

Ý tưởng đầy hứa hẹn nhất mà tôi có là sử dụng một đại biểu ẩn danh để nói cho lớp MyRowObject cách truy cập trực tiếp các thuộc tính, nhưng nếu có thể tôi không thể có được ngữ nghĩa ngay (được đặt tên của thuộc tính được lưu trữ trong một chuỗi, có cách nào tôi có thể viết một đóng cửa để truy cập trực tiếp tài sản đó?).

Vì vậy, có cách nào tốt đẹp để tránh sử dụng phản chiếu để điền ListView của tôi mà không làm mất bất kỳ chức năng nào không?

Phần mở rộng nguồn mở của ListView bị giới hạn do chính sách của công ty.

+0

gì về chuyển đổi/trường hợp? –

+0

Biểu thức (đặc biệt là trên .net 4) có thể là một cách dễ dàng để thực hiện một đại biểu nhanh chóng làm những gì bạn muốn. – CodesInChaos

Trả lời

3

Bạn có thể sử dụng các chức năng 2

private List<Func<T, string>> BuildItemGetters<T>(IEnumerable<PropertyInfo> properties) 
    { 
     List<Func<T, string>> getters = new List<Func<T, string>>(); 
     foreach (var prop in properties) 
     { 
      var paramExp = Expression.Parameter(typeof(T), "p"); 

      Expression propExp = Expression.Property(paramExp, prop); 
      if (prop.PropertyType != typeof(string)) 
       propExp = Expression.Call(propExp, toString); 

      var lambdaExp = Expression.Lambda<Func<T, string>>(propExp, paramExp); 

      getters.Add(lambdaExp.Compile()); 
     } 

     return getters; 
    } 

    private string[] GetItems<T>(List<Func<T, string>> properties, T obj) 
    { 
     int count = properties.Count; 
     string[] output = new string[count]; 

     for (int i = 0; i < count; i++) 
      output[i] = properties[i](obj); 

     return output; 
    } 

Gọi BuildItemGetters (xin lỗi vì cái tên, không thể nghĩ về bất cứ điều gì;) một lần với một danh sách các thuộc tính bạn muốn nhận được từ các hàng. Sau đó, chỉ cần gọi GetItems cho mỗi hàng. Trường hợp obj là hàng và danh sách là hàng bạn nhận được từ hàm khác.

Đối T chỉ cần sử dụng tên lớp của Row của bạn, như:

var props = BuildItemGetters<MyRowObject>(properties); 
string[] items = GetItems(props, row); 

ofcourse, chỉ gọi xây dựng khi các cột thay đổi

0

Hãy xem Reflection.Emit. Với điều này, bạn có thể tạo mã khi đang chạy để truy cập một thuộc tính cụ thể. Bài viết CodeProject này có một mô tả thú vị về cơ chế: http://www.codeproject.com/KB/cs/fast_dynamic_properties.aspx.

Tôi chưa xem xét kỹ mã của dự án, nhưng ấn tượng đầu tiên của tôi là ý tưởng cơ bản có vẻ đầy hứa hẹn. Tuy nhiên, một trong những cải tiến mà tôi sẽ thực hiện là một số phần của lớp phải tĩnh và chia sẻ, ví dụ: InitTypes và cụm động được tạo. Đối với phần còn lại, có vẻ như nó phù hợp với những gì bạn đang tìm kiếm.

1

BindingSourcePropertyDescriptor những kỹ thuật thanh lịch hơn để thực hiện thủ công đĩa dữ liệu ràng buộc, đó là nhiều hơn hoặc ít hơn những gì bạn đang làm với ListView khi nó ở trong VirtualMode. Mặc dù nó thường sử dụng sự phản chiếu nội bộ dù sao, bạn có thể dựa vào nó để làm việc hiệu quả và liền mạch.

Tôi đã viết một bài viết trên blog gần đây mà giải thích một cách chi tiết làm thế nào để sử dụng các cơ chế này (mặc dù đó là trong một bối cảnh khác nhau, các nguyên tắc đều giống nhau) - http://www.brad-smith.info/blog/archives/104

+0

Cảm ơn bài đăng của bạn, nhưng tôi đã thử cách này và hiệu suất vẫn giữ nguyên. – jtabak

0

Tôi không biết đủ về C# để cho bạn biết nếu điều đó là có thể, nhưng tôi sẽ đi và hack theo cách của tôi với một cái gì đó như thế này:

  • một lần, tôi sẽ cố gắng để có được 'con trỏ đại biểu' cho mọi thành viên mà tôi cần, và sẽ làm điều đó thông qua phản ánh - nếu nó là C++, những con trỏ đó sẽ là bù đắp vtable cho hàm getter thuộc tính
  • sẽ tạo một bản đồ với chuỗi-> con trỏ bù đắp
  • sẽ sử dụng bản đồ để gọi hàm getter trực tiếp thông qua con trỏ.

Có, nó có vẻ như một phép thuật, nhưng tôi đoán rằng ai đó có đủ kiến ​​thức CLR/MSIL có thể làm sáng tỏ điều đó nếu nó có thể từ xa.

+0

Tại sao trả lời nếu bạn thực sự không biết làm thế nào để làm điều đó? Mọi người đều có thể đoán .. – jgauffin

0

Đây là một biến thể khác trong bộ nhớ đệm của các phương thức nhận cho mỗi thuộc tính.

public class PropertyWrapper<T> 
    { 
     private Dictionary<string, MethodBase> _getters = new Dictionary<string, MethodBase>(); 

     public PropertyWrapper() 
     { 
      foreach (var item in typeof(T).GetProperties()) 
      { 
       if (!item.CanRead) 
        continue; 

       _getters.Add(item.Name, item.GetGetMethod()); 
      } 
     } 

     public string GetValue(T instance, string name) 
     { 
      MethodBase getter; 
      if (_getters.TryGetValue(name, out getter)) 
       return getter.Invoke(instance, null).ToString(); 

      return string.Empty; 
     } 
    } 

để có được một giá trị tài sản:

var wrapper = new PropertyWrapper<MyObject>(); //keep it as a member variable in your form 

var myObject = new MyObject{LastName = "Arne"); 
var value = wrapper.GetValue(myObject, "LastName"); 
+0

Tôi nghĩ anh ta đang cố gắng tránh những lời kêu gọi do các vấn đề tốc độ – Doggett

+0

Vâng, việc sử dụng các đại biểu phải nhanh hơn việc gọi phương thức GetValue của các thuộc tính. – jgauffin

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