94310


(具体的なコードに変更しました)  
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


CC BY-SA 4.0
2023-03-24T10:32:58.473
94297
2023-03-25T06:05:58.733
2023-03-25T03:25:55.180
57660
57660
question
powershell
HashTableの派生クラスを作ると、追加した変数がHashTableのKeyとして扱われる。これの回避方法は?


さしあたり(問題含みですが)現象を回避する方法を見つけたので自己回答しておきます。なお、この方法以外のもっと良い方法をご存知でしたら是非ご教授ください。

HashTableのKeysに対するアクセッサをオーバーライドすることで、Keysに対するアクセス結果に干渉できる。

[code]

        [System.Collections.ICollection]get_Keys(){
           $keys =([HashTable]$this).get_keys()
           return([System.Collections.ICollection]($keys.where{ $_ -ne ***ExclueNeeded*** }))
        }
    
    
[/code]

(この方法だと、特定の文字列を含めることのできないHashTableになってしまうため、十分な策とは言えません)


CC BY-SA 4.0
2023-03-24T14:20:52.637
94300
2023-03-24T14:20:52.637
57660
94297
answer


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

[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`にキーを追加することなく、プロパティに値を代入できるのではないでしょうか。


CC BY-SA 4.0
2023-03-25T06:05:58.733
94310
2023-03-25T06:05:58.733
4236
94297
answer