2010-11-03 86 views
5

我有一个虚拟模式下的ListView,底层数据存储在List<MyRowObject>中。 ListView的每一列对应于MyRowObject的公共字符串属性。我的ListView的列可以在运行时进行配置,这样它们中的任何一个都可以被禁用,并且它们可以被重新排序。要返回ListViewItemRetrieveVirtualItem事件,我也有类似的方法:有没有一种很好的方法来避免使用反射来填充我的虚拟ListView?

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; 
    } 
} 

RetrieveVirtualItem的事件处理类似如下:

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

也许并不奇怪,基准测试表明,该方法是显著比直接以硬编码顺序访问属性的实现慢,而且速度很慢,我想找到更好的解决方案。

我最有希望的想法是使用匿名委托来告诉MyRowObject类如何直接访问属性,但如果可能的话,我无法获得正确的语义(给定一个属性的名称存储在字符串中,有没有一种方法可以编写闭包来直接访问该属性?)。

那么,有没有一种很好的方法来避免使用反射来填充我的ListView而不会丢失任何功能?

由于公司政策,ListView的开放式源代码扩展名已关闭。

+0

怎么样的switch/case? – 2010-11-03 22:19:49

+0

表达式(尤其是在.net 4上)可能是一种简单的方法来实现一个快速的委托,完成你想要的任务。 – CodesInChaos 2010-11-03 22:46:56

回答

3

你可以使用这两个函数

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; 
    } 

调用BuildItemGetters(遗憾的名称,也想不出任何东西),其具有要从行得到的属性列表一次。然后,为每一行调用GetItems。其中obj是行,列表是从另一个函数获得的。

对于T只需用行的类名,如:

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

ofcourse,只能调用时所建的列更改

0

看看Reflection.Emit。有了这个,您可以即时生成访问特定属性的代码。这个CodeProject文章有一个有趣的机制描述:http://www.codeproject.com/KB/cs/fast_dynamic_properties.aspx

我并没有全面回顾项目的代码,但我的第一印象是基本想法看起来很有希望。然而,我要做的改进之一是该类的某些部分应该是静态的并且是共享的,例如InitTypes和创建的动态程序集。其余的,它看起来适合你要找的东西。

1

BindingSourcePropertyDescriptor是进行人工数据 - 更优雅的技术绑定,这与ListViewVirtualMode中的效果差不多。虽然它通常在内部使用反射,但您可以依靠它来高效无缝地工作。

我写了最近一篇博客文章,其详细解释了如何使用这些机制(尽管它在不同的情况下,原则都是一样的) - http://www.brad-smith.info/blog/archives/104

+0

感谢您的博文,但我尝试过,表现保持不变。 – jtabak 2010-11-04 17:27:09

0

我不知道有足够的了解C#来告诉你如果这是可能的,但我去破解我的方式有这样的事情:

  • 一次,我会试图让“委托指针”为每一个我需要的成员,并且将做到这一点通过反射 - 如果它是C++,那些指针将是属性获取函数的虚表偏移量
  • 将创建一个带有字符串 - >指针偏移量的地图
  • 将使用该地图直接通过指针调用getter函数。

是的,它看起来像是一种魔法,但我认为有足够CLR/MSIL知识的人可以在这种情况下说明,如果它是远程可能的。

+0

为什么回答如果你真的不知道该怎么做?每个人都可以猜到.. – jgauffin 2010-11-04 09:29:00

0

这是另一个变体,它为每个属性缓存get方法。

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; 
     } 
    } 

得到一个属性值:

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

我认为他试图避免由于速度问题引起的调用 – Doggett 2010-11-04 12:31:01

+0

那么,使用委托应该比调用GetValue方法的属性更快。 – jgauffin 2010-11-04 12:39:12

相关问题