1

In .Net 4.5 I have lists which contain objects that are time-consuming to load. A list of 5000 objects takes about 5 seconds to load, so for each loops take quite a long time to execute.

Since the initialisation of the objects is what takes all the time I was wondering if there is a type of list, or a way to make a list like this, where the objects are loaded progressively when they are needed.

Something like a list which would know how many objects there are, but which only instantiates objects when they are retrieved. Does this exist? Or is it possible to make a list like that?

I am looking for advice as to how to get this done (if it is doable), so bot C# or VB.Net code would be fine. Hence I added both tags.

EDIT

I should add that I have tried playing with lazy-loading, but that only seems to defer the loading of the list, which is not really a solution.

EDIT

Here is what I am using now. Note that I removed my lame attempts to use lazy loading and reverted back to the way I used to do it yesterday (which is effectively lazy loading without a Lazy object)

Public Class SISOverlayItemList : Inherits SISBaseObject : Implements IEnumerable(Of SISOverlayItem)

Default Public ReadOnly Property Item(ByVal Index As Integer) As SISOverlayItem
    Get
        Return New SISOverlayItem(Me.List(Index).ID, Me.Parent)
    End Get
End Property

Public ReadOnly Property Items As List(Of SISOverlayItem)
    Get
        Dim output As New List(Of SISOverlayItem)

        Call Me.List.Items.AsParallel.ForAll(Sub(i) output.Add(New SISOverlayItem(i.ID, Me.Parent))

        Return output
    End Get
End Property

Public ReadOnly Property Parent As SISOverlay
    Get
        Return Me._Parent
    End Get
End Property

Private ReadOnly Property List As SISList
    Get
        If Me._List Is Nothing Then
            Me._List = Me.Application.Lists(Me.Name)
        End If
        Call Me.Refresh()
        Return Me._List
    End Get
End Property

Private _List As SISList
Private _Parent As SISOverlay

Public Sub New(ByVal Parent As SISOverlay)

    Me._Parent = Parent
    Me._Application = Parent.Application
    Me._Name = String.Format("Overlay_{0}_Items", Parent.Name)
    Call Me.Refresh()

End Sub

Public Sub Refresh()

    Call Me.Application.Lists(Me.Name).ScanOverlay(Me.Parent)

End Sub

Public Function GetEnumerator() As IEnumerator(Of SISOverlayItem) Implements IEnumerable(Of SISOverlayItem).GetEnumerator

    Return Me.Items.GetEnumerator()

End Function

Private Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator

    Return Me.GetEnumerator

End Function

End Class

The Items property which converts all objects from List to items of the current list (SISOverlayItem) is what is taking so long. What is taking all the time in the conversion if that the ID parameter which is needed to instantiate an SISOverlayItem requires an API call to the Application object stored in each item of List.

If it were possible to execute these calls in small batches the or something it should remove the long delay required to instantiate every object in the list.

yu_ominae
  • 2,975
  • 6
  • 39
  • 76
  • c# or vb.net? You can't have both...What you describe is indeed usually implemented as lady loading. If it doesn't work for you, you're probably doing it wrong. Can't be more specific without more details from you... – walther Feb 05 '13 at 23:52
  • How is the 5 second initialization causing you problems? Are you trying to update/populate a UI? Just wondering what angle to look at – Rob Goodwin Feb 05 '13 at 23:56
  • I'm looking for a solution in either C# or VB.Net. I'll adapt whatever I get. I tried to lazy load the whole list, `Lazy(Of List(Of MyObject))`. That really didn't change anything. I also tried `List(Of Lazy(Of MyObject))`, which loaded the correct number of items quite rapidly, but all items had value `Nothing` so I couldn't enumerate. I've never done this before so I'm not sure what I did wrong in the second variant though. – yu_ominae Feb 05 '13 at 23:59
  • 1
    Can you post some code, please, so that we can analyze it and look, where the problem could come from. – zeyorama Feb 06 '13 at 00:00
  • @RobGoodwin 5 seconds is for a test list with about 5000 objects, but there can be several 10s of thousands of objects in a list and that would be quite a long delay. If there is no other way I'll take the hit, but if it was possible to just make this faster it would make me feel nice and fuzzy inside. – yu_ominae Feb 06 '13 at 00:01
  • @zeyorama I'll post what I have at the moment in a second. – yu_ominae Feb 06 '13 at 00:02
  • Lazy loading is useful only in a situation where the load is optional, or usually happens at some later time. The only way to load X items faster is with a faster processor, disk, etc. But you can optimise by using a paging model to load only a subset of list items. If your list is a UI control, load enough to fill the visible list area; then scrolling down will load the next page. Or you can load the complete list on a worker thread so the UI does not freeze during load. But you cannot load a UI control on a non-UI thread. – groverboy Feb 06 '13 at 00:10
  • What is the list used for? Are you using all the elements, or just querying from it? If possible, a better option is to filter the list to only the elements that are needed before they are instantiated. – Scott Wegner Feb 06 '13 at 00:16
  • @ScottWegner I will be enumerating the whole list and also executing queries against it. It represents all geometric items in an overlay, but can only be generated using an intermediary list (unfortunately). The problem is that it can change at any time and there is no way to know when it has, so it has to be regenerated every time before use to make sure it is up to date and that amounts to a lot of time. – yu_ominae Feb 06 '13 at 00:23
  • @groverboy A paging system is what I was thinking of. But I have no idea how I would go about implementing that? Is there something that can be put in the enumerator to get the list populated bit by bit as needed? – yu_ominae Feb 06 '13 at 00:25
  • @Kristian I appreciate your editing my question, but I am looking for an answer in C# or VB.Net. What can be done in one can be done in the other. Just because I program in VB.Net for this project doesn't mean on VB.Net programmers should look at it. – yu_ominae Feb 06 '13 at 00:41
  • The concept exists as a `LazyList`. I found some references that seem to be F#, but the idea is to cache entries already retrieved in another list. – mellamokb Feb 06 '13 at 00:44
  • @mellamokb From what I have read so far it doesn't look like it works any different from a Lazy(Of List(Of T)) construct, so doesn't look like it will do the trick. Thanks for the suggestion though. – yu_ominae Feb 06 '13 at 01:11
  • I don't know about `LazyLoad` but _virtual collection_ is another term for this kind of thing. If possible, change the type of property `Items` from `List(Of SISOverlayItem)` to `VirtualList(Of SISOverlayItem)`. Then (C# here!) `VirtualList` is your custom class that implements `IList`. `VirtualList` needs to implement at least the indexer property `this[int index]`, more if you need to support sorting. The indexer property will return an `SISOverlayItem`, instantiating the object only when required. – groverboy Feb 06 '13 at 01:24
  • Or you could reimplement `SISOverlayItemList` as a virtual collection, where the Item property (equivalent to `this[int index]`) returns an `SISOverlayItem`, again instantiating the object on demand. – groverboy Feb 06 '13 at 01:32
  • @groverboy Thanks, that also sounds like a good idea, but also sounds more difficult to try out than Charlies answer. I'll give it a try if Charlies suggestion doesn't work (once I get to translate all that stuff to C#...) – yu_ominae Feb 07 '13 at 01:20
  • True, Charlie's suggestion looks much cooler and should work if this applies here: "I tend to use yield-return when I calculate the next item in the list (or even the next group of items)." [Proper Use of yield return](http://stackoverflow.com/questions/410026/proper-use-of-yield-return) – groverboy Feb 07 '13 at 01:59

1 Answers1

2

It sounds like the objects do not all exist yet, and that is part of what's taking so long. If you do not need to have the collection of items as a list, It would be worth looking into yield return. This gives an IEnumerable that can be queried, filtered, or enumerated.

It might look something like this:

public readonly IEnumerable<SISOverlayItem> Items
{
    get
    {
        SISOverlayItem myItem = intermediateList.DoSomeWork();
        yield return myItem;
    }
}

This might be impractical. Also, if there is a lot of processing involved, this might be better suited as a method rather than a property. If you know how you will filter the collection, you could do something like:

public IEnumerable<SISOverlayItem> ProduceSelectedItems()
{
    var intermediateItems = from item in intermediateList
                            where item.isSelected
                            select item;
    foreach (var item in intermediateItems)
    {
        yield return item.DoSomeWork();
    }
}

Sorry I don't have Visual Studio on hand to check, but I think something like that might help. Another question outlining this approach is here.

Community
  • 1
  • 1
Charlie
  • 705
  • 1
  • 5
  • 14
  • Thanks that looks promising. I'll have to translate my project into C# first though, because implementing `yield` in VB.Net looks like a real pain in the neck. I'll let you know where I get at when I get a chance to performs the translation. – yu_ominae Feb 06 '13 at 04:04
  • Quick update. I finished the translation. Unfortunately the code I just found out that I can't just use what I've written. There are some other classes in that project, which are written in VB.Net (straight VB6 port, not written by me), they're a total mess and would take me a week to sort out because I have no clue what they even do. So I'll have to concentrate on getting the other stuff done first, sorry. – yu_ominae Feb 06 '13 at 06:57
  • Sounds like difficult code to build on! I'm glad you found something that works. – Charlie Feb 07 '13 at 00:19