我读过Lazy Initialization但实际上我不明白如何初始化里面的对象。
此函数返回LazyList
,这是一种Lazy类型的自定义实现
Public Function GetMethods(ByVal Assembly As String,
ByVal TypeName As String) 'As List(Of MethodDef) ' As MethodDef()
Dim methods As LazyList(Of MethodDef) = Nothing
Using ass As ModuleDefMD = ModuleDefMD.Load(Assembly)
For Each t As TypeDef In ass.GetTypes
If t.HasMethods AndAlso t.Name.String.Equals(TypeName, StringComparison.OrdinalIgnoreCase) Then
methods = t.Methods
' MsgBox(t.Methods.GetType.Name) ' Result: LazyList'1
Exit For
End If
Next t
Return methods
End Using
End Function
当我尝试读取LazyList的任何项的属性时,我得到NullReferenceException
异常,我想这是因为该对象未初始化? '因为注意lazylist项目计数是“2”,我完全确定我尝试读取的属性不能为空。
Imports dnlib.DotNet
Imports dnlib.DotNet.Emit
Imports dnlib.Utils
Dim methods As LazyList(Of MethodDef) = GetMethods("C:\WindowsApplication.exe", "Main")
MsgBox(methods.IsInitialized(0)) ' Result: False
MsgBox(methods.Count) ' Result: 2
For Each method As MethodDef In methods
' NullReferenceException exception here:
MsgBox(method.Name)
' NullReferenceException exception here too (reading m.hasbody):
If method.HasBody Then
Dim sb As New System.Text.StringBuilder
With sb
.AppendLine(String.Format("Method Instructions: {0}", Environment.NewLine &
String.Join(Environment.NewLine, method.Body.Instructions)))
End With
Debug.WriteLine(sb.ToString)
End If
Next method
如果我尝试重现我在上面的代码中所做的事情(尝试阅读属性),但在函数内部,所有内容都按预期运行,没有任何空引用异常......就像在这个例子中一样:
public function GetMethods (...)
' deleted code...
If t.HasMethods AndAlso t.Name.String.Equals(TypeName, StringComparison.OrdinalIgnoreCase) Then
methods = t.Methods
For Each m In methods
MsgBox(m.Name) ' no exceptions
MsgBox(m.HasBody) ' no exceptions
Next
End If
' deleted code...
end function
PS:导入来自dnlib库。
更新:
我想用更多更好的例子来解释这个问题。
在继续解释之前,有两个细节:
我确保在所有示例中都存在TypeName
参数并找到iis,我确保函数返回的对象集合永远不会为空,它的Collection.Count为 2 < / strong>正如我在问题开头所解释的那样,所以这就是问题所在。
好吧,下一个函数返回一个对象列表,这些对象带有一个名为HasBody
的属性,但此属性始终为空(抛出 NullReference 例外)当它不应为空时,该集合中包含的两个项的值应为 True 。
Public Function GetMethods(ByVal Assembly As String,
ByVal TypeName As String) As List(Of MethodDef)
Dim methods As List(Of MethodDef) = Nothing
Using ass As ModuleDefMD = ModuleDefMD.Load(Assembly)
For Each t As TypeDef In ass.GetTypes
If t.HasMethods AndAlso t.Name.String.Equals(TypeName, StringComparison.OrdinalIgnoreCase) Then
methods = t.Methods.ToList
Exit For
End If
Next t
Return methods
End Using
End Function
另一方面,如果我在函数中执行一个荒谬的修改(请参阅与tmp
对象相关的更改),该函数将返回初始化HasBody
属性的对象列表,非空,并且返回列表中包含的2个项目为HasBody
属性,其值为 True 。
Public Function GetMethods(ByVal Assembly As String,
ByVal TypeName As String) As List(Of MethodDef)
Dim methods As List(Of MethodDef) = Nothing
Dim tmp As Object
Using ass As ModuleDefMD = ModuleDefMD.Load(Assembly)
For Each t As TypeDef In ass.GetTypes
If t.HasMethods AndAlso t.Name.String.Equals(TypeName, StringComparison.OrdinalIgnoreCase) Then
methods = t.Methods.ToList
For Each m In methods
tmp = m.HasBody
Next
Exit For
End If
Next t
Return methods
End Using
End Function
那么这里的问题在哪里以及如何修复它?也许这是这种情况的时间解决方案,但返回函数的methoddef
对象包含很多属性,我需要访问更多属性比将来HasBody
所以我真的无法将每个属性“分配”给函数中的tmp
对象以便以丑陋的方式解决...
并且在两种情况下用于循环返回List的代码都是这样的:
注意:请记住,使用第一个函数我无法解析method.hasbody
属性,而method.body.instructions
属性都不会抛出NullReference异常。
但是使用修改后的函数,只有在返回List之前我已将其分配给函数内的method.hasbody
变量,我才能解析tmp
属性,{{1}也是如此。 }如果做了同样的话。
method.body.instructions
答案 0 :(得分:1)
我认为这是第一个问题:
Public Function GetMethods(ByVal Assembly As String,
ByVal TypeName As String) 'As List(Of MethodDef)
指定返回类型,它可能有效:As LazyList(Of MethodDef)
在大会中找不到TypeName
时也会发生这种情况。在这种情况下,您的LazyList仍然是Nothing并导致NRE。您应该检查返回 - 我不认为它与LazyList(还)有关:
Dim methods As LazyList(Of MethodDef) = _
GetMethods("C:\WindowsApplication.exe", "Main")
If methods IsNot Nothing Then
' iterate
测试床:
Dim methods As LazyList(Of MethodDef) = Nothing
点击按钮:
methods = Test_Handler("Test2")
For Each meth As MethodDef In methods
If meth.HasBody Then
Console.WriteLine("{0} has a body", meth.Name)
Else
Console.WriteLine("{0} has NO body", meth.Name)
End If
Next
测试处理程序基本上就是你所拥有的&amp; = As LazyList(Of MethodDef)
。输出:
.ctor has a body
testfunction has a body
Foo has a body
Bar has a body
ziggy has a body
接下来可能会出现更大的问题: ...我不明白如何初始化中的对象
您在LazyList中存储的是这些MethodDef
个对象 - 它们已经被实例化。 TypeDef.Methods
实际上返回了MethodDef
的IList,LazyList涉及的唯一原因是因为您的代码以这种方式存储它们。
如果您想调用他们描述的方法,我很确定您不能使用MethodDef
作为起点。它们只是描述方法特征的元数据。您已经知道它们来自TypeName
,因此要调用其中一个方法,您必须拥有该类型的实例。
它未知的你想要做什么,但是对于元数据集合来说,LazyList似乎是不必要的。
对象浏览器中Methods
的定义为IList
,因为来源也显示:
public ThreadSafe.IList<MethodDef> Methods
在内部, it 使用LazyList。这可能是因为它可以为类型创建和保存方法列表,但不会为它们加载元数据,除非并且直到实际询问有关方法列表的内容。 然后它们被初始化。
这同样有效:
Private Function Test_Handler(typeName As String) As List(Of MethodDef)
Dim modDef As ModuleDefMD = ModuleDefMD.Load("C:\Temp\ConsoleApplication1.exe")
Dim methods As New List(Of MethodDef)
For Each t As TypeDef In modDef.GetTypes
' stupid way to drill, but will work for demo purposes
If t.Name.Equals(typeName) Then
'methods = t.Methods
methods.AddRange(t.Methods.ToArray)
Exit For
End If
Next
Return methods
End Function
你可以变得太聪明并尝试做:Return t.Methods
并获得关于LastList强制转换为List的强制转换错误。你得到的方法对象集已经被实例化了,所以如果你得到NRE,可能会有其他东西搞砸了。
这解决了问题:
Using ass As ModuleDefMD = ModuleDefMD.Load(Assembly)
方法列表defs在内部是一个LazyList,并且在调用它们的属性或方法之前不会被实例化。为了实例化它们(显然)需要访问创建它们的东西。所以引用一些无害的东西,这样你就可以处理ModuleDefMD
Dim methods As New List(Of MethodDef)
Dim b As Boolean
Using modDef As ModuleDefMD = ModuleDefMD.Load("C:\Temp\ConsoleApplication1.exe")
For Each t As TypeDef In modDef.GetTypes
' stupid way to drill, but will work for demo purposes
If t.Name.Equals(typeName) Then
For Each m As MethodDef In t.Methods
b = m.HasBody
methods.Add(m)
Next
Exit For
End If
Next
End Using
Return methods
这或多或少是你Tmp对象所做的。你称它为丑陋,但由于源是一个LazyList,它可以是或使用modDef
引用计算出来的东西。