当尝试在C#中使用代理以功能方式解决问题时,我遇到了一个我想要分享的陷阱。我希望听到你的建议。
背景
我想从一个对象列表中填充一个网格,其中使用委托来获取单个列的值(从Philip Pipers ObjectListView control借来的想法)。
此外,我想自动插入包含两个值之间(数字)差异的列。
所以我的对象具有属性FirstValue
,SecondValue
和ThirdValue
我希望列有FirstValue
,(SecondValue-FirstValue)
,SecondValue
,{ {1}},(ThirdValue-SecondValue)
。
我已经调整了现有的网格控件以在对象列表上使用委托,这部分工作正常。
首次尝试
首先,我尝试了类似的事情:
ThirdValue
问题
在函数式语言中,以这种方式计算差异可以正常工作,因为新的委托(在class MyGridClass : DelegateGrid
{
DelegateGrid.ValueGetter lastGetter;
public MyGridClass() {
AddMyColumn(delegate(MyObj obj) { return obj.FirstValue; });
AddMyColumn(delegate(MyObj obj) { return obj.SecondValue; });
AddMyColumn(delegate(MyObj obj) { return obj.ThirdValue; });
}
private void AddMyColumn(DelegateGrid.ValueGetter getter) {
if (lastGetter != null)
base.AddColumn(new DelegateColumn(delegate(MyObj obj) {
return getter(obj)-lastGetter(obj);
}));
base.AddColumn(new DelegateColumn(getter));
}
};
内构建)将使用AddMyColumn
的值施工时间。但是在C#中,新委托使用引用到lastGetter
,因此在执行时,它使用执行时的实际值。因此,差异将始终针对最后一列(即lastGetter
)。
解决方案
我自己找到的一个解决方案是
obj.ThirdValue
请注意
public AddMyColumn(DelegateGrid.ValueGetter getter) {
if (lastGetter != null) {
DelegateGrid.ValueGetter newLastGetter =
new DelegateGrid.ValueGetter(lastGetter);
base.AddColumn(new DelegateColumn(delegate(MyObj obj) {
return getter(obj)-newLastGetter(obj);
}));
}
// ...
}
不会解决问题。
问题
已经找到了解决方案,这部分有点形式,但是
答案 0 :(得分:7)
问题只是捕获变量而不是值。这是一个大致相同的解决方案,但稍微简单一点:
public AddMyColumn(DelegateGrid.ValueGetter getter) {
if (lastGetter != null) {
DelegateGrid.ValueGetter newLastGetter = lastGetter;
base.AddColumn(new DelegateColumn(delegate(MyObj obj) {
return getter(obj)-newLastGetter(obj);
}));
}
// ...
}
基本上没有必要创建一个新的委托实例 - 委托是不可变的,所以你只需要通过赋值复制该值。
就捕获的值而言,这不是一个特定于委托的问题 - 这通常是匿名方法和lambda表达式的常见问题。典型的例子是;
List<Action> actions = new List<Action>();
for (int i=0; i < 10; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (Action action in actions)
{
action();
}
这打印“10”10次。要打印0-9,您需要再次更改捕获变量的范围:
List<Action> actions = new List<Action>();
for (int i=0; i < 10; i++)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}
foreach (Action action in actions)
{
action();
}
答案 1 :(得分:0)
为了回答你的其他观点,lambda语法会使它变得更好,因为它们会减少冗长的代码。
delegate(MyObj obj) {
return getter(obj)-newLastGetter(obj);
}
变为:
obj => getter(obj)-newLastGetter(obj)