有没有一种很好的方法可以避免使用反射来填充我的虚拟ListView?

时间:2010-11-03 22:03:00

标签: c# winforms reflection

我在虚拟模式下有ListView,基础数据存储在List<MyRowObject>中。 ListView的每一列对应于MyRowObject的公共字符串属性。我的ListView列可以在运行时配置,这样就可以禁用它们中的任何一个,并且可以对它们进行重新排序。要为ListViewItem事件返回RetrieveVirtualItem,我的方法类似于:

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的开源扩展程序是禁用的。

5 个答案:

答案 0 :(得分: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);

当然,只在列改变时调用构建

答案 1 :(得分:1)

BindingSourcePropertyDescriptor是执行手动数据绑定的更优雅的技术,当ListView位于{{1}时,它或多或少与VirtualMode相关。 }}。虽然它通常在内部使用反射,但您可以依靠它有效且无缝地工作。

我最近写了一篇博客文章,详细解释了如何使用这些机制(虽然它在不同的背景下,原则是相同的) - http://www.brad-smith.info/blog/archives/104

答案 2 :(得分:0)

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

我还没有彻底审查过该项目的代码,但我的第一印象是基本理念看起来很有希望。但是,我要做的一个改进是类的一些部分应该是静态的并且是共享的,例如InitTypes和创建的动态程序集。对于其他人来说,它看起来很适合你想要的东西。

答案 3 :(得分:0)

我不太了解c#告诉你这是否可行,但我会用这样的方式破解我的方式:

  • 一次,我将尝试为我需要的每个成员获取'委托指针',并将通过反射来实现 - 如果它是c ++,那些指针将是属性getter函数的vtable偏移
  • 将创建一个包含string-&gt;指针偏移
  • 的地图
  • 将使用map直接通过指针调用getter函数。

是的,它似乎是一种魔力,但我想有足够的CLR / MSIL知识的人可以在远程可能的情况下阐明它。

答案 4 :(得分: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");