懒惰的初始化混乱

时间:2015-03-15 20:23:03

标签: .net vb.net collections ienumerable lazy-initialization

方案

我试图读取.Net托管资源文件中包含的资源,即ResX文件。

我设计了这个简单的类,它将用于存储/构建资源集合:

Public Class Resource

    Public Property Name As String

    Public Property Data As Object

    Public ReadOnly Property Type As Type
        Get
            Return Data.GetType
        End Get
    End Property

End Class

所以,通过这个功能,我获得了资源:

Public Iterator Function GetResources() As IEnumerable(Of Resource)

    ' Read the ResX file.
    Using resX As New Resources.ResXResourceSet(Me.filePath1)

        ' Get the resource enumerator.
        Dim resXDictionay As IDictionaryEnumerator = resX.GetEnumerator()

        ' Iterate the resources.
        Do While resXDictionay.MoveNext()
            Yield New Resource With {.Name = CStr(resXDictionay.Key),
                                     .Data = resXDictionay.Value}
        Loop

    End Using ' resX 

End Function

访问资源的方式是这样的公共财产:

Public ReadOnly Property Resources As IEnumerable(Of Resource)
    Get
        Return GetResources()
    End Get
End Property

问题

所有这一切的问题是,例如ResX文件包含BIG文件大小的资源,然后当我像这样迭代文件时,应用程序的内存消耗增加了该文件大小(以及更多):

    Dim resX As New ResXManager(".\MyResources.resx"))

    For Each res As ResXManager.Resource In resX.Resources

        Debug.WriteLine(res.Name)
        ' Debug.WriteLine(res.Type.ToString)
        ' Debug.WriteLine(res.Data.ToString)

    Next res

请注意,在上面的代码res.Data中有注释,根本没有使用/读取,但是当资源很大时会增加内存消耗,例如,如果资源大小为50 mb,那么此刻我使用应用程序内存上方的循环增加了超过50 MB。

为了避免此问题,我记得Lazy类型,我不太了解它但不应该初始化res.Data内容,因为我不需要阅读/使用它在上面的循环中,我是对的吗?

然后,我如何调整上面的代码以返回Lazy列表?

我已经尝试过这种方式,但我确实错过了一些东西:

Public ReadOnly Property Resources As Lazy(Of Resource)
    Get
        Return New Lazy(Of Resource)(Function() GetResources())
    End Get
End Property

它抛出了关于强制转换的编译器错误,我理解它,但我认为Lazy是某种类似IEnumerable的集合:

  

Option Strict On禁止隐式转换   ' System.Collections.Generic.IEnumerable(中   WindowsApplication2.ResXManager.Resource)'至   ' WindowsApplication2.ResXManager.Resource'

问题

我怎么能以正确的方式做到这一点?。

简而言之,我希望有Resource.Data的可访问引用,但我想避免读取/初始化其内容(增加内存消耗),直到我真正需要访问/使用该属性。

我希望你能理解我。

1 个答案:

答案 0 :(得分:1)

无法帮助你(不是我的主要专业领域)..不过,我已经做了一些寻找...我认为如果你使用ResXResourceReader,那么你像When using the ResXResourceReader how can tell if the resource is an embedded file or if it is an embedded string一样设置UseResXDataNodes = True,那么您应该能够在不加载资源的情况下加载资源描述。

然后您可以使用GetValue方法懒洋洋地加载资源,如下所示:

Dim rsxr As ResXResourceReader = New ResXResourceReader("Resource1.resx") With { .UseResXDataNodes = True }

For Each de As DictionaryEntry In rsxr
    Dim node As ResXDataNode = CType(de.Value, ResXDataNode)

    ' And then, when you need it
    Dim obj As Object = node.GetValue(DirectCast(Nothing, ITypeResolutionService))
Next

请注意,有两种GetValue方法,要将Nothing传递给其中一种方法,您必须DirectCast方法。

现在......我已经在VB.NET中编写了一个几乎完整的例子:

Public Class Resource
    Public Property Name As String

    Public Property Node As ResXDataNode

    Public ReadOnly Property Data As Object
        Get
            Return Node.GetValue(DirectCast(Nothing, ITypeResolutionService))
        End Get
    End Property

    Public ReadOnly Property Type As Type
        Get
            If (Node.FileRef Is Nothing) Then
                Return GetType(String)
            End If

            Return System.Type.GetType(Node.FileRef.TypeName)
        End Get
    End Property
End Class

Public Iterator Function GetResources() As IEnumerable(Of Resource)
    ' Read the ResX file.
    Using rsxr As ResXResourceReader = New ResXResourceReader(Me.filePath1) With {.UseResXDataNodes = True}
        For Each de As DictionaryEntry In rsxr
            Yield New Resource With {.Name = DirectCast(de.Key, String),
                                     .Node = DirectCast(de.Value, ResXDataNode)}
        Next
    End Using
End Function

像以前一样使用它。 Data按需加载。计算Type时不会加载Data