CodeAnalysis可以返回CA2202的误报吗?或者我的代码确实有问题?

时间:2015-11-24 23:11:28

标签: vb.net visual-studio code-analysis

我遇到了同样的问题,解释了 here ,但正在迭代EnvDTE.Processes

在我链接用户的问题@ Plutonix 确认这是一个错误警告,我认为他参考obj.Getenumerator()提及所以我认为我的问题将被视为错误但是,如果这是一个错误的警告,我想知道的不仅仅是一个肯定,而是说这是一个错误的警告。

这是警告:

  

CA2202不要多次处置对象Object   ' procs.GetEnumerator()'在方法中可以多次丢弃   ' DebugUtil.GetCurrentVisualStudioInstance()&#39 ;.为了避免产生   System.ObjectDisposedException你不应该调用Dispose   一次在物体上:线条:   214 Elektro.Application.Debugging DebugUtil.vb 214

这是代码,procs对象是警告中涉及的对象,但我没有看到任何一次性对象:

Public Shared Function GetCurrentVisualStudioInstance() As DTE2

    Dim currentInstance As DTE2 = Nothing
    Dim processName As String = Process.GetCurrentProcess.MainModule.FileName
    Dim instances As IEnumerable(Of DTE2) = DebugUtil.GetVisualStudioInstances
    Dim procs As EnvDTE.Processes

    For Each instance As DTE2 In instances

        procs = instance.Debugger.DebuggedProcesses

        For Each p As EnvDTE.Process In procs

            If (p.Name = processName) Then
                currentInstance = instance
                Exit For
            End If

        Next p

    Next instance

    Return currentInstance

End Function

PS:请注意,代码块取决于其他成员,但它们与此问题无关。

1 个答案:

答案 0 :(得分:5)

简短版本:对我来说,这看起来像代码分析组件中的错误。

很长的版本(嘿,你哄我花了我下午和晚上的大部分时间来解读这个,所以你不妨花一点时间阅读它:))...


我做的第一件事就是看IL。与我的猜测相反,它确实在同一个对象上包含对Dispose()的多次调用。这个理论非常重要。

但是,该方法包含对Dispose()的两个单独调用,仅适用于不同的对象。到这个时候,我已经确信这是一个错误。我已经看到在处理一个类实例"拥有的相关类时触发CA2202的提及。另一个类的实例,并且两个实例都被处理掉了。虽然不方便且值得压制,但在这些情况下警告似乎是有效的;其中一个对象真的被处理了两次。

但在这种情况下,我有两个独立的IEnumerator个对象;一个人不拥有,也没有与另一个人有关。处置一个不会处置另一个。因此,代码分析是错误的警告。但具体是什么令人困惑呢?

经过多次实验,我想出了这个近乎极少的代码示例:

Public Class A
    Public ReadOnly Property B As B
        Get
            Return New B
        End Get
    End Property
End Class

Public Interface IB
    Function GetEnumerator() As IEnumerator
End Interface

Public Class B : Implements IB
    Public Iterator Function GetEnumerator() As IEnumerator Implements IB.GetEnumerator
        Yield New C
    End Function
End Class

Public Class C
    Dim _value As String
    Public Property Value As String
        Get
            Return _value
        End Get

        Set(value As String)
            _value = value
        End Set
    End Property
End Class

Public Shared Function GetCurrentVisualStudioInstance2() As A
    For Each a As A In GetAs()
        For Each c As C In a.B
            If (c.Value = Nothing) Then
                Return a
            End If
        Next c
    Next a

    Return Nothing
End Function

Public Shared Iterator Function GetAs() As IEnumerable(Of A)
    Yield New A()
End Function

这会产生您在其他代码示例中看到的相同的虚假CA2202。有趣的是,对接口IB的声明和实现的微小改动导致警告消失:

Public Interface IB : Inherits IEnumerable
End Interface

Public Class B : Implements IB
    Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Yield New C
    End Function
End Class

不知何故,代码分析因IEnumerable的非GetEnumerator()实现而感到困惑。 (更奇怪的是,您使用的实际类型,DTE API中的Processes接口,都继承IEnumerable 声明自己的{{1}方法......但后者是代码分析混淆的根源,而不是组合。)

有了这个,我试图在C#中重现这个问题,发现我做不到。我编写了一个C#版本,其结构与VB.NET版本中的类型和方法完全相同,但它在没有警告的情况下通过了代码分析。所以我再次看了IL。

我发现C#编译器生成的代码与VB.NET编译器非常相似,但不完全相同。特别是,对于保护每个循环返回的GetEnumerator()的{​​{1}} / try块,这些循环的所有初始化都在外部执行 {{阻止,而在VB.NET版本中,初始化是在内部执行的。

显然,这也足以阻止代码分析对一次性物品的使用感到困惑。


鉴于它似乎是VB.NET的finally实现与嵌套循环的结合,一种解决方法就是以不同的方式实现该方法。无论如何我更喜欢LINQ语法,这里是你的方法的LINQified版本,它编译时没有代码分析警告:

IEnumerator

完整性,C#版本(因为所有这些代码都是在C#实现转换为VB.NET后开始扩展以处理"当前实例"案例):

try