Есть ли хороший способ избежать использования отражения для заполнения моего виртуального ListView? - PullRequest
5 голосов
/ 04 ноября 2010

У меня есть 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 ]

3 голосов
/ 04 ноября 2010

Вы можете использовать эти 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;
    }

Вызовите BuildItemGetters (извините за имя, ничего не могу придумать;) один раз со списком свойств, которые вы хотите получить из строк.Затем просто вызовите GetItems для каждой строки.Где obj - строка, а список - тот, который вы получили от другой функции.

Для T просто используйте имя класса вашей строки, например:

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

ofcourse,вызывать сборку только при изменении столбцов

1 голос
/ 04 ноября 2010

BindingSource и PropertyDescriptor - более изящные методы для выполнения привязки данных вручную, что более или менее похоже на то, что вы делаете с ListView, когда он находится в VirtualMode. Хотя в любом случае он обычно использует отражение внутри, вы можете положиться на него, чтобы работать эффективно и без проблем.

Недавно я написал статью в блоге, в которой подробно объясняется, как использовать эти механизмы (хотя в другом контексте принципы одинаковы) - http://www.brad -smith.info / blog / archives / 104

0 голосов
/ 04 ноября 2010

Вот еще один вариант кэширования методов 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 голосов
/ 04 ноября 2010

Я не знаю достаточно о c #, чтобы сказать вам, если это возможно, но я пойду и взломаю свой путь с чем-то вроде этого:

  • один раз, я попытаюсь получить 'делегировать указатели для каждого члена, который мне нужен, и сделает это с помощью отражения - если бы это был c ++, эти указатели были бы смещениями vtable для функции получения свойства
  • создаст карту со строкой-> смещение указателя
  • будет использовать карту для вызова функции получения прямо через указатель.

Да, это похоже на магию, но я предполагаю, что кто-то с достаточным знанием CLR / MSIL может пролить светна это, если это возможно удаленно.

0 голосов
/ 04 ноября 2010

Взгляните на Reflection.Emit.При этом вы можете генерировать код на лету, который обращается к определенному свойству.В этой статье CodeProject есть интересное описание механизма: http://www.codeproject.com/KB/cs/fast_dynamic_properties.aspx.

Я не подробно рассмотрел код проекта, но у меня первое впечатление, что основная идея выглядит многообещающей.Однако одно из улучшений, которое я хотел бы сделать, состоит в том, что некоторые части класса должны быть статическими и совместно используемыми, например InitTypes и созданная динамическая сборка.В остальном, похоже, что он соответствует тому, что вы ищете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...