【问题标题】:Is it Possible to Lazy Load & Type Cast the Object in Single Iteration?是否可以在单次迭代中延迟加载和类型转换对象?
【发布时间】:2017-10-23 15:03:46
【问题描述】:

我是 c# 编程新手,有以下场景。

我正在使用一个返回 IEnumerable 的 API,我想根据一些对象属性对其进行迭代:

IEnumerable<objects> listOfObjects = filter.getItems(id);
 List<CustomObject> sortedList = new List<CustomObject>();
            foreach (CustomObject obj in listOfObjects )
            {
                obj.Load(Load.Expanded);
                sortedList.Add(obj);
            }
 foreach (CustomObject custObj in sortedList.OrderByDescending(c => c.RevisionDate))
            {
                // business logic
            }

我需要执行以上所有操作,因为我无法对过滤器查询中返回的对象进行类型转换。此外,从过滤器查询返回的对象未加载,这意味着如果我不执行第一个 foreach 循环,则第二个 foreach 中的 RevisionDate 值将为空。

我想知道是否有更好的方法来处理这种情况,是否可以通过 1 个循环消除这些行数?

【问题讨论】:

  • 你这样做的方式非常好。如果它没有加载 obj 状态的副作用,它可以被简化。我认为目前的情况很好。
  • 首先,您只需将.OfType&lt;CustomObject&gt;() 添加到第一行的末尾即可进行转换。其次,如果您使用的框架没有加载对象,那么请使用另一个框架。
  • @PeterMorris:不,您使用.Cast&lt;CustomObject&gt;() 进行投射。您使用OfType 进行过滤。它不同的原因是因为如果你不小心让listOfObjects 有一些NotCustomObject,那么你的人会默默地过滤掉它们,从而隐藏你的问题,而Cast 方法会抛出异常,因为你有错误的对象.
  • @Chris 如果您假设您想要一个错误,那么是的,如果您假设您不想要,那么不是。我想这是信息不足的情况。
  • @Chris - 看起来他正在使用某种对象持久性框架,尽管他提供了他知道的对象的 ID,但他的检索器正在返回一个基类而不是他想要的后代类属于那个子类。他说他是 C# 的新手,这让我期待他使用“cast”这个词来解释他的要求,因为尽管使用了“过滤器”,但他不知道任何替代方法(如过滤)。如果我们做出假设,那么任何一个都可能是正确的。正如我所说,这是not enough information 的一个案例,可以确定其中任何一个都是正确的。

标签: c# linq foreach


【解决方案1】:

您可以像这样在一个 linq 语句中做到这一点:

foreach (var custObj in listOfObjects
                       .Cast<CustomObject>()
                       .Select(obj => {obj.Load(); return obj;})
                       .OrderByDescending(c => c.RevisionDate))

请注意,Select 的这种用法通常是不鼓励的,这不是一个很好的做法(即在 Select 中有类似 obj.Load 的副作用)。

【讨论】:

    【解决方案2】:

    您应该能够使用 IEnumerable 方法Select

    请参阅文档here

    简而言之,您需要执行以下操作来消除第一个循环:

    List<CustomObject> sortedList = filter.getItems(id).Select<object, CustomObject>(x => 
              {
                 (CustomObject)x).Load(Load.Expanded); 
                 return (CustomObject)x;  
               });
    

    【讨论】:

    • 我认为您的代码存在编辑问题(您的第一行可能想要被删除?)原始帖子中也没有迹象表明Load 实际上返回了该对象。它肯定被用作void返回类型。
    • eesh,这对我来说很难看。更正以删除垃圾行并将 lambda 设置为将 Load 视为 void return。
    【解决方案3】:

    Thera 是 C# 库中的几个特殊结构,旨在处理延迟初始化。每个都可能有一些特性,可能更适合或更适合您的需求。

    第一个是System.Lazy&lt;T&gt; 类,它比没有类开销的System.Threading.LazyInitializer 具有更好的性能,它带有一堆静态方法,并提供线程特定的数据System.Threading.ThreadLocal&lt;T&gt;

    Lazy&lt;T&gt; 的用法很简单:

    // Initialize by using default Lazy<T> constructor. The 
    // Orders array itself is not created yet.
    Lazy<Orders> _orders = new Lazy<Orders>();
    
    // Initialize by invoking a specific constructor on Order which
    // will be used when Value property is accessed
    Lazy<Orders> _orders = new Lazy<Orders>(() => new Orders(100));
    
    // Lazy<Orders> will create the array only if displayOrders is 
    // which will go through path where _orders.Value is accessed
    if (displayOrders == true)
    {
        DisplayOrders(_orders.Value.OrderData);
    }
    else
    {
        // Don't waste resources getting order data.
    } 
    

    您可以将 lambda 转换为 CustomObject,而不是使用构造函数传递 lambda。如果您追求最佳性能,请避免使用 Linq 并以程序方式进行。

    示例来自:Lazy Initialization Microsoft Docs,为清楚起见,我更改了 cmets,几乎可以直接应用于您的代码

    【讨论】:

    • 也许您可以根据 OP 的情况调整您的示例?我不确定您在什么时候将IEnumerable&lt;Object&gt; 转换为您的惰性类... Ienumerable 中的每个对象是否都替换为惰性对象,或者整个IEnumerable 是否被Lazy 替换? obj.Load(Load.Expanded) 在什么时候被调用?而且我也不清楚引入 Lazy 是否真的比只为每个对象调用 .Load 更好......
    猜你喜欢
    • 2014-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 2012-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多