延迟初始化混乱

Lazy initialization confusion

场景

我正在尝试读取 .Net 托管资源文件(ResX 文件)中包含的资源。

我设计了这个简单的 class,它将为我提供 store/struct 资源集合:

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 属性 这样的:

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

问题

问题在于,例如 ResX 文件包含大文件大小的资源,然后当我像这样迭代文件时,应用程序的内存消耗会增加该文件大小(甚至更多):

    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,根本不是used/read,但是当资源很大时会增加内存消耗,例如资源大小为50 mb然后在我使用上面的循环的那一刻,应用程序的内存增加了 50 多 mb。

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

那么,我如何将上面的代码修改为 return 一个 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 disallows implicit conversions from 'System.Collections.Generic.IEnumerable(Of WindowsApplication2.ResXManager.Resource)' to 'WindowsApplication2.ResXManager.Resource'

问题

我怎样才能以正确的方式做到这一点?

简而言之,我想获得 Resource.Data 的可访问引用,但我只想避免 reading/initializing 它的内容(这会增加内存消耗),直到我真的需要 access/use 属性。

希望你能理解我。

帮不了你太多(不是我的主要专业领域)。不过,我已经做了一些调查...我认为如果你使用 ResXResourceReader,然后设置 UseResXDataNodes = True 喜欢这里 When using the ResXResourceReader how can tell if the resource is an embedded file or if it is an embedded string 那么你应该可以在不加载资源的情况下加载资源的描述。

然后你可以使用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.

的情况下计算的