添加到 System.Collections 的数组时,不能对空值表达式调用方法

You cannot call a method on a null-valued expression when adding to array of System.Collections

我有一个函数 (powershell 5.1),它使用正则表达式从使用相同正则表达式的 3 个单独 files/strings 中获取信息。我想在同一个函数中处理所有 3 个,并且 return 它作为 System.Collections 的数组(也许是 Hashtable ...不确定是否需要)。但我收到上述错误。这样做对我来说很有意义,否则我将有不同的函数使用相同的正则表达式做完全相同的事情。所以我试图重新使用功能在 3 个不同的 strings/files.

上执行相同的正则表达式

我查了一下错误,它说有些东西没有分配。 not assigned 我没有看到我的案例中没有分配什么。

Function ProcessOmAlarmXml{
[cmdletbinding()]
  Param () 
  Process
  {
   $OMs = [System.Collections]::new() #this will store the 3 hashtables from the regex

   $pathViewBase = 'C:\EndToEnd_view\' 
   $XML_OmMap_Dirs = @('\OmAlarmMap.xml')   #will add the other 2 later
   $XML_OmMap_Names = @('Config1','Config2','Config3')
   $i = 0
   
   #get each one from array
   foreach($omFile in $XML_OmMap_Dirs)
   {
      $pathToXml = Join-Path -Path $pathViewBase -ChildPath $omFile
      if(Test-Path $pathToXml)
      {
          #get file contents to parse as string
          $fileContent = Get-MethodContents -codePath $pathToXml -methodNameToReturn "<Configuration>" -followingMethodName "</Configuration>"

          #get the Mapping from omMap xml file
          $errorMap = @{}
          # create array to collect keys that are grouped together
          $keys = @()

          #regex works perfectly
          switch -Regex ($fileContent -split '\r?\n') {  #test regex with https://regex101.com/
            'Name=\"(\w+-\w+)' { #12-5704
                # add relevant key to key collection
                $keys = $Matches[1] } #only match once
                '^[\s]+<Value Name="MsgTag[">]+(\w+[.\w]*)<\/Value' { # gets the word after MsgTag, before closing tag      (\w+)<\/Value                MsgTag[">]+(\w+)<\/Value    ([?:<!\s\S]?"MsgTag[">]+)(\w+[.\w]*)<\/Value #fails if line commented out..still captures line
                # we've reached the relevant error, set it for all relevant keys
                foreach($key in $keys){
                    #Write-Host "om key: $key"
                    $errorMap[$key] = $Matches[1]
                    Write-Host "om key: $key ... value: $($errorMap[$key])"
                }
            }
            'break' {
                # reset/clear key collection
                $keys = @()
            }    
          }#switch
          
          #I'm trying to add each $errorMap with the name in the array $XML_OmMap_Names so I can pull them out and use them later meaningfully
          [void]$OMs.Add({$XML_OmMap_Names[$i]=$errorMap})  #gives error message
          Write-Host $OMs.Count
          Write-Host $OMs -ForegroundColor Cyan
          $i++
       }#test-Path
       else
       {
          Write-Host "No such path $pathToXml"
       }
       
      } #foreach
   return $errorMap #will return $OMs later instead
  } #end Process
}# End of Function

我也在尝试将我的对象存储在数组中,就像这样似乎正在使用:arrays

请注意,我试图在这里提供最少量的信息,并希望充分展示该函数的作用,这样就可以理解为什么我需要将哈希表存储在数组中 return 无需重新编写复杂的代码,这仍然非常复杂。如果有人有更多关于如何将数组中的哈希表存储到数组中的所有 3 return 的信息,那就太好了。

有关错误的更多信息:

You cannot call a method on a null-valued expression.
At C:\Users21\temp endToEnd folder while network down\EndToEndParser.ps1:329 char:11
+           [void]$OMs.Add({$XML_OmMap_Names[$i]=$errorMap})  #[System. ...
+           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

回答

你想要

using namespace system.collections.generic
$OMs = [list[object]]::new() #this will store the 3 hashtables from the regex

[object[]]$XML_OmMap_Names = @('Config1','Config2','Config3')

$OMs.Add({$XML_OmMap_Names[$i]=$errorMap})
$OMs.Add( $somethingElse )

[System.Collections] 不是有效类型。这就是为什么这是空的。

$OMs = [System.Collections]::new()
$null -eq $OMs # returns True

注意,@()不是数组构造函数

 @('\OmAlarmMap.xml')

要将它们声明为列表,而不是意外地变成标量,您需要的是给它一个类型。

$XML_OmMap_Names = @('Config1','Config2','Config3')

# becomes
[object[]]$XML_OmMap_Names = @('Config1','Config2','Config3')

  • 左侧的类型 将声明类型,它们是强类型的。
  • 右侧的类型 强制值,但不将它们声明为任何类型。

左手边或右手边,表示它在赋值 = 运算符的哪一边。

using namespace system.collections.generic
$OMs = [list[object]]::new() #this will store the 3 hashtables from the regex

$OMs.Add({$XML_OmMap_Names[$i]=$errorMap})
$OMs.Add( $somethingElse )

创建数组:应避免什么

你要避免的是

1] [System.Collections.ArrayList][System.Collections.Generic[Type]]::new()

这在why generics are better? : microsoft docs

中有解释

2] 像 $s = @()$s += stuff 这样的加法,因为每次加法都会分配新的内存。随着大小的增加,它会呈指数级恶化。

你应该做什么

1] 文档说 对于具有混合类型的对象,使用 [list[object]]

using namespace System.Collections.Generic

$items = [list[object]]::new()
$items.Add( '1' )
$items.Add( (Get-Item . ))

2] 对于一种特定类型的对象使用 [list[type]]

$nums = [list[Int64]]::new()
$nums.add( '1' )

$nums[0] -is [Int64]  # True
$nums[0] -is [string] # False

3] 如果你不是 += ing

,隐式数组是可以的
# now the type is an array, regardless if nothing, or single value return
[object[]]$ArrayOfAnything = FunctionMightReturnNothing # 0-tomany items


[object[]]$AlwaysArray = Get-ChildItem . | Select -first 1 
$AlwaysArray.GetType() # type is object[] arrray

$sometimesArray = Get-ChildItem . | select -first 1 
$sometimesArray.GetType() # type is a scalar of type [filesysteminfo]

隐式数组很好。

# often in powershell it's more natural to emit output to the pipeline, instead of manually adding arrays. 

$results = @(
    Get-ChildItem c:\ -depth 3 | Sort-Object LastWriteTime | Select-Object -top 3    
    if($IncludeProfile) { 
         Get-ChildItem -file "$Env:UserProfile" -depth 3 | Sort-Object Length | Select-Object -top 3
    }   
)

链接

听起来你要找的是一个(可能有序的)嵌套 hashtable:

# Initialize an ordered hashtable.
$OMs = [ordered] @{ } # This will later store 3 nested hashtables.

# The (top-level) keys
$XML_OmMap_Names = @('Config1','Config2','Config3')

$i = 0
foreach($someFile in 'foo', 'bar', 'baz') {
  # Construct the nested hashtable
  $errorMap = [ordered] @{ nested = $someFile }
  # Add it to the top-level hashtable.
  $OMS.Add($XML_OmMap_Names[$i], $errorMap)
  ++$i
}

请注意键 $XML_OmMap_Names[$i] 和值 $errorMap 如何作为 单独的参数 传递给 .Add() 方法。
(相比之下,在您的尝试 [void]$OMs.Add({$XML_OmMap_Names[$i]=$errorMap}) 中,您错误地将 单个 参数作为 script block ({ ... }) 传递。)

或者,索引语法 - $OMS[$XML_OmMap_Names[$i]] = $errorMap - 可用于添加条目;请注意,此方法会悄悄地用相同的键替换任何预先存在的值,而 .Add() 方法会抛出异常。

现在您可以通过名称获取条目;例如:

$OMs.Config1  # Same as: $OMs['Config1']

注意:由于使用了[ordered],所以也可以使用positional indices:

$OMs[0] # Same as : $OMs.Config1

也可以直接访问嵌套哈希表的条目:

$OMs.Config1.nested  # Same as: $OMs['Config1']['nested'] -> 'foo'