jta*_*bak 5 c# reflection winforms
我有一个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;
}
}
Run Code Online (Sandbox Code Playgroud)
RetrieveVirtualItem外观的事件处理程序类似于:
private void listView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
e.Item = new ListViewItem(_virtualList[e.ItemIndex].GetItems(_currentColumns));
}
Run Code Online (Sandbox Code Playgroud)
也许并不奇怪,基准测试表明这种方法比直接以硬编码顺序访问属性的实现要慢得多,并且减速非常显着,我希望找到更好的解决方案.
我最有希望的想法是使用匿名委托来告诉MyRowObject类如何直接访问属性,但如果有可能我无法正确获取语义(给定存储在字符串中的属性的名称,是有没有办法可以编写一个闭包来直接访问该属性?).
那么,有没有一种很好的方法可以避免使用反射来填充我的ListView而不会丢失任何功能?
由于公司政策,ListView的开源扩展是不受限制的.
你可以使用这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;
}
Run Code Online (Sandbox Code Playgroud)
使用您想要从行中获取的属性列表调用一次 BuildItemGetters(对不起,这个名字,想不出任何东西;)。然后只需为每一行调用 GetItems 即可。其中 obj 是行,列表是从其他函数获得的行。
只需T使用 Row 的类名,例如:
var props = BuildItemGetters<MyRowObject>(properties);
string[] items = GetItems(props, row);
Run Code Online (Sandbox Code Playgroud)
当然,仅在列更改时调用构建