如何解决这个Lambda表达式外部变量问题? [英] How do I get around this lambda expression outer variable issue?

查看:876
本文介绍了如何解决这个Lambda表达式外部变量问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用PropertyDescriptor和ICustomTypeDescriptor(

I'm playing with PropertyDescriptor and ICustomTypeDescriptor (still) trying to bind a WPF DataGrid to an object, for which the data is stored in a Dictionary.

由于如果将WPF DataGrid的Dictionary对象列表传递给它,它将基于字典的公共属性(比较器,计数,键和值)自动生成列,而Person则将Dictionary子类化,并实现ICustomTypeDescriptor.

Since if you pass WPF DataGrid a list of Dictionary objects it will auto generate columns based on the public properties of a dictionary (Comparer, Count, Keys and Values) my Person subclasses Dictionary and implements ICustomTypeDescriptor.

ICustomTypeDescriptor定义了一个GetProperties方法,该方法返回一个PropertyDescriptorCollection.

ICustomTypeDescriptor defines a GetProperties method which returns a PropertyDescriptorCollection.

PropertyDescriptor是抽象的,因此您必须对其进行子类化,我认为我有一个采用Func的构造函数和一个代表字典中值的获取和设置的Action参数.

PropertyDescriptor is abstract so you have to subclass it, I figured I'd have a constructor that took Func and an Action parameters that delegate the getting and setting of the values in the dictionary.

然后我为字典中的每个键创建一个PersonPropertyDescriptor,如下所示:

I then create a PersonPropertyDescriptor for each Key in the dictionary like this:

            foreach (string s in this.Keys)
            {
                var descriptor = new PersonPropertyDescriptor(
                        s,
                        new Func<object>(() => { return this[s]; }),
                        new Action<object>(o => { this[s] = o; }));
                propList.Add(descriptor);
            }

问题在于,每个属性都具有自己的Func和Action,但它们都共享外部变量 s ,因此尽管DataGrid会自动为"ID","FirstName","LastName",他们都获得了年龄",性别"并设置为性别",这是foreach循环中 s 的最终静止值.

The problem is that each property get's its own Func and Action but they all share the outer variable s so although the DataGrid autogenerates columns for "ID","FirstName","LastName", "Age", "Gender" they all get and set against "Gender" which is the final resting value of s in the foreach loop.

如何确保每个委托使用所需的字典键,即在实例化Func/Action时的s值?

How can I ensure that each delegate uses the desired dictionary Key, i.e. the value of s at the time the Func/Action is instantiated?

有很多义务.

剩下的就是我的想法,我只是在这里做实验,这些不是真正的"课程...

Here's the rest of my idea, I'm just experimenting here these are not 'real' classes...

// DataGrid binds to a People instance
public class People : List<Person>
{
    public People()
    {
        this.Add(new Person());
    }
}

public class Person : Dictionary<string, object>, ICustomTypeDescriptor
{
    private static PropertyDescriptorCollection descriptors;

    public Person()
    {
        this["ID"] = "201203";
        this["FirstName"] = "Bud";
        this["LastName"] = "Tree";
        this["Age"] = 99;
        this["Gender"] = "M";        
    }        

    //... other ICustomTypeDescriptor members...

    public PropertyDescriptorCollection GetProperties()
    {
        if (descriptors == null)
        {
            var propList = new List<PropertyDescriptor>();

            foreach (string s in this.Keys)
            {
                var descriptor = new PersonPropertyDescriptor(
                        s,
                        new Func<object>(() => { return this[s]; }),
                        new Action<object>(o => { this[s] = o; }));
                propList.Add(descriptor);
            }

            descriptors = new PropertyDescriptorCollection(propList.ToArray());
        }

        return descriptors;
    }

    //... other other ICustomTypeDescriptor members...

}

public class PersonPropertyDescriptor : PropertyDescriptor
{
    private Func<object> getFunc;
    private Action<object> setAction;

    public PersonPropertyDescriptor(string name, Func<object> getFunc, Action<object> setAction)
        : base(name, null)
    {
        this.getFunc = getFunc;
        this.setAction = setAction;
    }

    // other ... PropertyDescriptor members...

    public override object GetValue(object component)
    {
        return getFunc();
    }

    public override void SetValue(object component, object value)
    {
        setAction(value);
    }
}

推荐答案

Marc的解决方案当然是正确的,但是我认为我将在下面的WHY上进行扩展.我们大多数人都知道,如果您在forforeach语句中声明一个变量,则该变量的生存期与内部变量的寿命一样长,这使得它似乎就像变量与在这样的语句的语句块中声明的变量,但这是不正确的.

Marc's solution is of course correct, but I thought I'd expand upon WHY below. As most of us know, if you declare a variable in a for or foreach statement, it only lives as long as what's inside, which makes it seem like the variable is the same as a variable declared in the statement-block of such a statement, but that's not right.

要更好地理解它,请使用以下for循环.然后,我将以while形式重新声明等效"循环.

To understand it better, take the following for-loop. Then I'll re-state the "equivalent" loop in a while-form.

for(int i = 0; i < list.Length; i++)
{
    string val;
    list[i] = list[i]++;
    val = list[i].ToString();
    Console.WriteLine(val);
}

这可以在while形式中实现,如下所示:(这并不完全相同,因为continue的行为不同,但是对于作用域规则,它是相同的)

This works out to in while-form like below: (it isn't exactly the same, because continue will act differently, but for scoping rules, it's the same)

{
    int i = 0;
    while(i < list.Length)
    {
        {
            string val;
            list[i] = list[i]++;
            val = list[i].ToString();
            Console.WriteLine(val);
        }
        i++;
    }
}

以这种方式分解"后,变量的范围变得更加清晰,您可以看到为什么它总是在程序中捕获相同的"s"值,以及为什么Marc的解决方案显示了将变量放置在何处,从而使每次都会捕获唯一的一个.

When "exploded" out this way, the scope of the variables becomes clearer, and you can see why it always captures the same "s" value in your program, and why Marc's solution shows where to place your variable so that a unique one is captured every time.

这篇关于如何解决这个Lambda表达式外部变量问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆