Хеш-таблица с бесконечной глубиной. Почему? Как избежать?

#powershell #powershell-4.0

#powershell #powershell-4.0

Вопрос:

Когда я использую переменную $emptyHashTable для создания пустой хэш-таблицы, кажется, что она генерирует хэш-таблицу бесконечной глубины. Не могу понять, почему.
Когда я использую @{}, он работает правильно.

Пример кода

 cls

$L1     = "L1"
$emptyHashTable = @{}

# Correct, hashtable contains 1 sub-hashtable
$proj1       = @{}
$proj1."$L1" = @{}

# Wrong, endless hashtable depth
$proj2       = @{}
$proj2."$L1" = $emptyHashTable

# Wrong, endless hashtable depth
$proj3       = $emptyHashTable
$proj3."$L1" = @{}

# Wrong, endless hashtable depth
$proj4       = $emptyHashTable
$proj4."$L1" = $emptyHashTable

Write-Host
Write-Host "proj1"
Write-Host "Level 0: " $proj1.GetType()
Write-Host "Level 1: " $proj1.L1.GetType()
Write-Host "Level 2: " $proj1.L1.L1.GetType() # Will generate error: You cannot call a method on a null-valued expression.
Write-Host
Write-Host "proj2"
Write-Host "Level 0: " $proj2.GetType()
Write-Host "Level 1: " $proj2.L1.GetType()
Write-Host "Level 2: " $proj2.L1.L1.GetType()
Write-Host "Level 3: " $proj2.L1.L1.L1.GetType()
Write-Host "Level 4: " $proj2.L1.L1.L1.L1.GetType()
Write-Host
Write-Host "proj3"
Write-Host "Level 0: " $proj3.GetType()
Write-Host "Level 1: " $proj3.L1.GetType()
Write-Host "Level 2: " $proj3.L1.L1.GetType()
Write-Host "Level 3: " $proj3.L1.L1.L1.GetType()
Write-Host "Level 4: " $proj3.L1.L1.L1.L1.GetType()
Write-Host
Write-Host "proj4"
Write-Host "Level 0: " $proj4.GetType()
Write-Host "Level 1: " $proj4.L1.GetType()
Write-Host "Level 2: " $proj4.L1.L1.GetType()
Write-Host "Level 3: " $proj4.L1.L1.L1.GetType()
Write-Host "Level 4: " $proj4.L1.L1.L1.L1.GetType()
  

Результат

 proj1
Level 0:  System.Collections.Hashtable
Level 1:  System.Collections.Hashtable
You cannot call a method on a null-valued expression.
At D:XandorraSQLbmsHashTableTest.ps1:25 char:1
  Write-Host "Level 2: " $proj1.L1.L1.GetType() # Will generate error: You cannot  ...
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      CategoryInfo          : InvalidOperation: (:) [], RuntimeException
      FullyQualifiedErrorId : InvokeMethodOnNull


proj2
Level 0:  System.Collections.Hashtable
Level 1:  System.Collections.Hashtable
Level 2:  System.Collections.Hashtable
Level 3:  System.Collections.Hashtable
Level 4:  System.Collections.Hashtable

proj3
Level 0:  System.Collections.Hashtable
Level 1:  System.Collections.Hashtable
Level 2:  System.Collections.Hashtable
Level 3:  System.Collections.Hashtable
Level 4:  System.Collections.Hashtable

proj4
Level 0:  System.Collections.Hashtable
Level 1:  System.Collections.Hashtable
Level 2:  System.Collections.Hashtable
Level 3:  System.Collections.Hashtable
  

Я использую Powershell 4.0
Мой $PSVersionTable:

 Name                           Value                                                               
----                           -----                                                               
PSVersion                      4.0                                                                 
WSManStackVersion              3.0                                                                 
SerializationVersion           1.1.0.1                                                             
CLRVersion                     4.0.30319.34014                                                     
BuildVersion                   6.3.9600.17400                                                      
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}                                                
PSRemotingProtocolVersion      2.2                                                                 
  

Ответ №1:

Вы упускаете из виду тот факт, что объекты передаются по ссылке, а не по значению.

Это структура вашего тестирования, которая омрачает эти результаты. Запускайте каждое отдельное $projX назначение в своем собственном экземпляре powershell, не запуская другие, и оно должно работать так, как ожидалось.

Например, когда вы пишете:

 $proj2."$L1" = $emptyHashTable

$proj3       = $emptyHashTable
$proj3."$L1" = @{}
  

вы установили $proj2.L1 ссылку на $emptyHashTable . Таким образом, вы также устанавливаете $proj3 ссылку на $proj2.L1 и, следовательно, на $emptyHashTable . Это означает, что $proj3.L1 = @{} это то же $proj2.L1.L1 = @{} самое, что и , и так далее.

Комментарии:

1. Я не уверен, что это правильно. Когда я устанавливаю $proj2.L1 в качестве ссылки на $emptyHashTable, вы могли бы сказать, что $proj2.L1 является указателем на $emptyHashTable . Указатель является односторонним. Итак, если я не ошибаюсь, $emptyHashTable не является ссылкой на $ proj2.L1 . Когда я затем присваиваю $emptyHashTable $proj3, тогда $proj3 является указателем на $emptyHashTable . Я не понимаю, как это может стать ссылкой (указателем) на $proj2.L1.

Ответ №2:

Похоже, это ошибка в Powershell v4.
Когда тот же код выполняется в Powershell v5, бесконечная глубина не возникает.