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

Ele*_*ios 3 vb.net code-analysis visual-studio

我遇到了同样的问题,这里解释但是迭代了EnvDTE.Processes.

在我链接的问题中,用户@ Plutonix确认这是一个虚假的警告,我认为他提到了这obj.Getenumerator()一点,所以我认为我的问题也将被视为虚假警告,但是,如果这是一个虚假的警告,我想知道的不仅仅是一种肯定,而是说它是一种虚假的警告.

这是警告:

CA2202不要多次处理对象对象'procs.GetEnumerator()'可以在方法'DebugUtil.GetCurrentVisualStudioInstance()'中多次处理.为避免生成System.ObjectDisposedException,不应在对象上多次调用Dispose:Lines: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
Run Code Online (Sandbox Code Playgroud)

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

Pet*_*iho 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
Run Code Online (Sandbox Code Playgroud)

这会产生与您在其他代码示例中看到的相同的虚假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
Run Code Online (Sandbox Code Playgroud)

不知何故,代码分析正在被非IEnumerable实现的混淆GetEnumerator().(更奇怪的是,您使用的实际类型,ProcessesDTE API中的接口,都继承IEnumerable 声明了自己的GetEnumerator()方法......但后者是代码分析混淆的根源,而不是组合).

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

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

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


鉴于它似乎是VB.NET的实现For Each和嵌套循环的组合,一种解决方法是以不同方式实现该方法.无论如何我更喜欢LINQ语法,这里是你的方法的LINQified版本,它编译时没有Code Analysis警告:

Public Shared Function GetCurrentVisualStudioInstance() As DTE2
    Dim processName As String = Process.GetCurrentProcess.MainModule.FileName

    Return GetVisualStudioInstances.FirstOrDefault(
        Function(instance)
            Return instance.Debugger.DebuggedProcesses.Cast(Of EnvDTE.Process).Any(
                Function(p)
                    Return p.Name = processName
                End Function)
        End Function)
End Function
Run Code Online (Sandbox Code Playgroud)

而且为了完整性,还有C#版本(因为所有这些代码都是在C#实现转换为VB.NET然后扩展以处理"当前实例"的情况下启动的):

public static DTE2 GetCurrentVisualStudioInstance()
{
    string processName = Process.GetCurrentProcess().MainModule.FileName;

    return GetVisualStudioInstances()
        .FirstOrDefault(i => i.Debugger.DebuggedProcesses
            .Cast<EnvDTE.Process>().Any(p => p.Name == processName));
}
Run Code Online (Sandbox Code Playgroud)

  • @ElektroStudios我刚刚在我自己的c#代码中遇到过这个问题.我通过使用`.ToArray()`来解决这个问题,这样我的`foreach`就不会(隐含地)使用`.GetEnumerator()`.这可能并不总是实际的,这取决于有多少数据,但它在我的情况下起作用. (3认同)