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

本文关键字:指针 方法 参数 存储 | 更新日期: 2023-09-27 18:16:42

类似于带有自由变量的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(,但我似乎不明白这是怎么做到的。。。

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

您的示例有点错误(这可能不会起作用,但语义/意图是正确的(:

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);
}

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

编译器通过用字段替换局部变量来生成闭包。因此,它看起来像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
  • 枚举函数的结果将创建另一个枚举器
  • 每次都会获得一个新的委托实例

因此,我认为出于性能原因而绑定重用闭包是没有用的。

        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属性