HashTableの派生クラスを作ると、追加した変数がHashTableのKeyとして扱われる。これの回避方法は?


(具体的なコードに変更しました)  
PowerShellで`HashTable`の派生クラスを作ろうとしています。

[code]

    class DerivedHT : HashTable{
        [ScriptBlock]$GetKeyBlock
    
        DerivedHT():base(){
            $this.GetKeyBlock = { $args[0] }
        }
    
        Add([object]$elem){
            if( $null -eq $this[$elem] ){
                $this[$elem] = @()
            }
            $this[$elem] += $elem
        }
    
        [int]OccurrencesOf([object]$elem){
            return(($this[$elem]).Count)
        }
    
        [object]Includes([object]$elem){
            return( $null -ne $this[$elem])
        }
    }
    
    
[/code]

このようなクラスを定義すると、プロパティ`GetKeyBlock`が自動的に`HashTable`の`Key`として扱われてしまうのですが、これを回避する方法はないでしょうか?

[code]

    $dht = [DerivedHT]::new()
    
    @(1,3,5,7,9).foreach{ $dht.Add($_) }
    @(3,7).foreach{ $dht.Add($_) }
    
    '----- keys -----' | Write-Host
    $dht.Keys | Write-Host
    
    '----- Values -----' | Write-Host
    $dht.Values | Write-Host
    
[/code]

実行結果  
\----- keys -----  
**GetKeyBlock**  
9  
7  
5  
3  
1  
\----- Values -----  
**$args[0]**  
9  
7 7  
5  
3 3  
1




原因がわかりました。再現コードをもう少し整理すると

[code]

    class DerivedHT : HashTable{
        [ScriptBlock]$GetKeyBlock
        Add([object]$elem){
            if( $null -eq $this[$elem] ){
                $this[$elem] = @()
            }
            $this[$elem] += $elem
        }
    }
    
    $dht = [DerivedHT]::new()
    $dht.GetKeyBlock = { $args[0] }  # コメントアウトすれば再現しない
    @(1,3,5,7,9).foreach{ $dht.Add($_) }
    @(3,7).foreach{ $dht.Add($_) }
    '----- keys -----' | Write-Host
    $dht.Keys | Write-Host
    '----- Values -----' | Write-Host
    $dht.Values | Write-Host
    
[/code]

で再現し、もちろん原因は `$dht.GetKeyBlock = { $args[0] }`
行を実行したことであり、ここをコメントアウトすれば発生しなくなります。

つまり、

> プロパティ`GetKeyBlock`が自動的に`HashTable`の`Key`として扱われてしまう

は誤解で、 **プロパティの存在** は問題ではなく、正しくは **プロパティへ代入を行うこと** が原因です。

* * *

PowerShellでは.NET本来の機能だけでなく、PowerShell独自の機能を追加するため、各クラスはPowerShellが派生クラスを作っています。プロパティに代入を行うと`HashTable`に追加されるのもその一つとなります。  
このことはドキュメントにも記されていて、

[about_Hash_Tables - プロパティ名の競合の処理](https://learn.microsoft.com/ja-
jp/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-7.3#handling-
property-name-collisions)

> キー名が `HashTable` 型のいずれかのプロパティ名と競合する場合は、 `psbase`組み込みメンバー
> を使用してこれらのプロパティにアクセスできます。

[about_intrinsic_members - 組み込みメンバーについて](https://learn.microsoft.com/ja-
jp/powershell/module/microsoft.powershell.core/about/about_intrinsic_members?view=powershell-7.3)も参照してください。

この説明に従い、

[code]

    $dht.GetKeyBlock = { $args[0] }
    
[/code]

を

[code]

    $dht.psbase.GetKeyBlock = { $args[0] }
    
[/code]

と改めれば、`HashTable`にキーを追加することなく、プロパティに値を代入できるのではないでしょうか。




原因がわかりました。再現コードをもう少し整理すると

[code]

    class DerivedHT : HashTable{
        [ScriptBlock]$GetKeyBlock
        Add([object]$elem){
            if( $null -eq $this[$elem] ){
                $this[$elem] = @()
            }
            $this[$elem] += $elem
        }
    }
    
    $dht = [DerivedHT]::new()
    $dht.GetKeyBlock = { $args[0] }  # コメントアウトすれば再現しない
    @(1,3,5,7,9).foreach{ $dht.Add($_) }
    @(3,7).foreach{ $dht.Add($_) }
    '----- keys -----' | Write-Host
    $dht.Keys | Write-Host
    '----- Values -----' | Write-Host
    $dht.Values | Write-Host
    
[/code]

で再現し、もちろん原因は `$dht.GetKeyBlock = { $args[0] }`
行を実行したことであり、ここをコメントアウトすれば発生しなくなります。

つまり、

> プロパティ`GetKeyBlock`が自動的に`HashTable`の`Key`として扱われてしまう

は誤解で、 **プロパティの存在** は問題ではなく、正しくは **プロパティへ代入を行うこと** が原因です。

* * *

PowerShellでは.NET本来の機能だけでなく、PowerShell独自の機能を追加するため、各クラスはPowerShellが派生クラスを作っています。プロパティに代入を行うと`HashTable`に追加されるのもその一つとなります。  
このことはドキュメントにも記されていて、

[about_Hash_Tables - プロパティ名の競合の処理](https://learn.microsoft.com/ja-
jp/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-7.3#handling-
property-name-collisions)

> キー名が `HashTable` 型のいずれかのプロパティ名と競合する場合は、 `psbase`組み込みメンバー
> を使用してこれらのプロパティにアクセスできます。

[about_intrinsic_members - 組み込みメンバーについて](https://learn.microsoft.com/ja-
jp/powershell/module/microsoft.powershell.core/about/about_intrinsic_members?view=powershell-7.3)も参照してください。

この説明に従い、

[code]

    $dht.GetKeyBlock = { $args[0] }
    
[/code]

を

[code]

    $dht.psbase.GetKeyBlock = { $args[0] }
    
[/code]

と改めれば、`HashTable`にキーを追加することなく、プロパティに値を代入できるのではないでしょうか。

