如何使用linq延迟执行为每个项目执行自定义函数?

时间:2012-10-31 22:07:52

标签: c# linq

我有一个:

public Dictionary<string,BaseModel> data  { get; set; }

我希望实现与现在工作的代码等效,只使用Linq延迟执行。

foreach (KeyValuePair<string, BaseModel> item in data)
{
   T model = (T)item.Value; //each item needs to be cast to T, T inherits from BaseModel
   model.Init(this, personId); //Init is a function I wrote I want to call on each item
   l.Add(model); //currently I am adding each item to a list, but IEnumerable<T> can work
}

我开始编写代码如下:

IEnumerable<T> l = Cache[type].Data.Cast<T>()
                                   .Select(item => item);

但我无法弄清楚如何在每个项目上调用Init函数(每个模型都有一个,它们都从BaseModel继承)。我一直在阅读关于谓词委托等的内容,但找不到如何做某事的例子(pseduocode):

IEnumerable<T> l = Cache[type].Data.Cast<T>()
                                   .Select(item => item)
                                   .RunMeOnEachItemLater(InitWrapperDelegate);

如果你想知道这是为了什么,我有一个MVC项目,我正在实现一个模型数据缓存。

4 个答案:

答案 0 :(得分:4)

别。强烈建议在功能样式中使用LINQ - 您的谓词不应该有副作用。这正是因为延迟执行 - 这意味着传递给LINQ方法的lambda只在需要时才会执行,如果有的话。

如果您想确保在IEnumerable的元素上调用一些副作用,请使用foreach

foreach (var model in Cache[type].Data.Select(i=>i.Value).Cast<T>()) {
    model.Init(...);
}

即便如此,如果您需要初始化的对象,我还是会考虑在字段或变量中缓存LINQ表达式的结果:

var models = Cache[type].Data.Select(i=>i.Value).Cast<T>().ToArray();
foreach (var model in models) {
    ...
}

原因是IEnumerable无法保证每次枚举时都返回相同的项目。显然,如果基础类型是一个集合,但如果您知道它是,那么您应该使用ICollection而不是IEnumerable来表达它。

如果您不能使Init幂等,以便每次从缓存中获取内容时都可以重新执行它,那么在将模型添加到缓存时,或者以其他方式清理其生命周期时,您应该初始化模型。

答案 1 :(得分:1)

您可以使用List(T).ForEach方法,如下所示:

var l = Cache[type].Data.Cast<T>().ToList().ForEach(InitWrapperDelegate);

答案 2 :(得分:1)

为什么不将伪代码转换为实际代码?

public static class MyExtensions
{
  public static IEnumerable<T> RunMeOnEachItemLater(this IEnumerable<T> sequence,
                                                    Action<T> action)
  {
      foreach(T item in sequence)
      {
         action(item);
         yield return item;
      }
  }
}

现在,您可以稍后使用LINQ延迟执行为每个项执行自定义函数:

IEnumerable<BaseModel> l = Cache[type].Data.Cast<BaseModel>()
                                           .RunMeOnEachItemLater(m => m.Init());

答案 3 :(得分:-1)

功能等同物是:

var list = data.Values.Cast<T>()
    .Select(x=>
    {
        x.Init(this, personId); 
        return x;
    })
    .ToList();

请注意,您可以删除.ToList()以返回IEnumerable<T>,并在枚举列表时稍后运行Init次调用。