存储指向方法参数的指针以供以后重用

时间:2011-12-02 12:21:11

标签: c# pointers lambda

类似于带有自由变量的lambda表达式如何工作,我想实现自己的闭包类来捕获一些方法参数。

public class Closure<TObject, TVariable>
{
    public TVariable Variable { get; set; }
    public Func<TObject, bool> Predicate { get; set; }
}

然后我有一些适用于DateTime实例的类。应该使用此闭包类的方法之一是:

// Original Version
public IEnumerable<Item> GetDayData(DateTime day)
{
    this.items.Where(i => i.IsValidForDay(day));
}

我想将其转换为使用Closure类。问题是我想重用(性能原因)我的Closure类实例:

private Closure<Item, DateTime> closure = null;
public IEnumerable<Item> GetDayData(DateTime day)
{
    if (closure == null)
    {
        this.closure = new Closure<Item, DateTime>() {
            Variable = reference of "day" param, <=== HOW ????????
            Predicate = i => i.IsValidForDay(this.closure.Variable)
        }
    }
    this.items.Where(this.closure.Predicate);
}

为了让我重用相同的Closure实例,我不得不将参数的值存储到闭包的Variable字段中),而是存储方法参数的引用指针。所以下次这个方法被称为闭包Variable时,实际上会指向要使用的正确值。

我该怎么做?

当编译器生成捕获lambda表达式自由变量的类时,也会发生类似的事情。我一直在看反编译代码(使用Reflector),但我似乎不明白这是怎么做的......

3 个答案:

答案 0 :(得分:0)

你的例子有点不对(这也许不行,但语义/意图是正确的):

private Closure<Item, DateTime> closure = null;
public IEnumerable<Item> GetDayData(DateTime day)
{
    if (closure == null)
    {
        this.closure = new Closure<Item, DateTime>() {
            Predicate = i => i.IsValidForDay(this.closure.Variable)
        }
    }
    // assign here, else you only capture it the first time
    closure.Variable = day;
    this.items.Where(this.closure.Predicate);
}

答案 1 :(得分:0)

您可以引用局部变量,但这些是托管引用,不能存储在字段中。

编译器生成的闭包通过用字段替换局部变量来工作。因此它看起来像C#级别的局部变量,但就像CLR级别的字段一样。

但在你的情况下,我认为没有理由你想通过引用捕获局部变量。所以应该可以做你想做的事情:

public class Closure<TObject, TVariable>
{
    public TVariable Variable { get; set; }
    public Func<TVariable, TObject, bool> OpenPredicate { get; set; }

    public bool ClosedPredicate(TObject o)
    {
      return OpenPredicate(this, o);
    }
}

private Closure<Item, DateTime> closure = null;
public IEnumerable<Item> GetDayData(DateTime day)
{
    if (closure == null)
    {
        this.closure = new Closure<Item, DateTime>() {
            OpenPredicate = (closure, i) => i.IsValidForDay(closure.Variable)
        }
    }
    closure.Variable=day;
    this.items.Where(this.closure.ClosedPredicate);
}

但我认为这段代码是个坏主意。如果性能如此重要,请抛弃LINQ,并自己动手。委托调用开销远大于创建一个或两个对象的成本。根据我的经验,与手写代码相比,LINQ通常会慢2-3倍。

此代码还会创建更多对象:

  • items.GetEumerator()返回一个,除非它是一个值类型或者保存了Enumerable。
  • Where会返回新的IEnumerable
  • 枚举函数的结果会创建另一个枚举器。
  • 每次都会获得一个新的委托实例

因此,我认为因为性能原因而重复使用闭包是没用的。

答案 2 :(得分:-1)

        List<PicInfo> pi = new List<PicInfo>();
        pi.Add(new PicInfo() { fileName = "a" });
        pi.Add(new PicInfo() { fileName = "a" });
        pi.Add(new PicInfo() { fileName = "b" });
        pi.Add(new PicInfo() { fileName = "b" });
        pi.Add(new PicInfo() { fileName = "a" });            

        string a = "a";
        Closure<PicInfo, string> cl = null;
        cl = new Closure<PicInfo, string>() { 
                  Variable = a, 
                  predicate = (i => i.fileName == cl.Variable.ToString()) 
        };
        MessageBox.Show(pi.Where(cl.predicate).Count().ToString()); // shows 3
        // Change variable value here
        string b = "b";
        cl.Variable = b;
        MessageBox.Show(pi.Where(cl.predicate).Count().ToString()); // Shows 2

@Robert Koritnik,这对我有用,with your existing class。如果只想使用现有实例更改变量,可以设置为Variable属性