如何在PowerShell中正确创建和修改元组的哈希集?

问题描述 投票:0回答:1

我想在PowerShell中创建一个2元素(对)元组的哈希集。

$MySet = New-Object System.Collections.Generic.HashSet[System.Tuple]

哪一个 看来 来工作。

$MySet.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     HashSet`1                                System.Object

$MySet.Add

OverloadDefinitions
-------------------
bool Add(System.Tuple item)
void ICollection[Tuple].Add(System.Tuple item)
bool ISet[Tuple].Add(System.Tuple item)

但如果我创建一个元组并将其添加到集合中,

$Tuple = [System.Tuple]::Create("Test", "Hello")
$MySet.Add($Tuple)
MethodException: Cannot find an overload for "Add" and the argument count: "1".

有什么方法可以正确地进行呢?

奖励:没有 哈希集的线程安全版本 - 有什么方法可以以线程安全的方式修改read哈希集?

powershell set hashset
1个回答
1
投票

这里的问题是 [System.Tuple]::Create($a,$b) 实际上创建了一个 [System.Tuple]但是 [System.Tuple[T1,T2]] 哪儿 T1 是一种 $aT2 是一种 $b.

虽然它们共用部分名称。[System.Tuple[T1,T2]]不可转让[System.Tuple]所以,我们必须为你的 [HashSet].

因为你的元组项目值是字符串,所以选择了 [System.Tuple[string,string]]:

$set = [System.Collections.Generic.HashSet[System.Tuple[string,string]]]::new()
$tuple = [System.Tuple]::Create("Hello", "World")
$set.Add($tuple)

附加答案

如果你走另一个方向,只是创建一个 HashSet[object] 为了实现尽可能广泛的可分配性,你可以创建一个线程安全的 ConcurrentSet 包裹 HashSet[object] 方法,然后在自定义的 powershell 类中使用 ReaderWriteLockSlim 以方便并发读而独占写。

using namespace System.Collections.Concurrent
using namespace System.Collections.Generic
using namespace System.Threading

# Custom IEqualityComparer based on [scriptblock]
# Use [PSComparer]::new({$args[0].Equals($args[1])}) to "restore" default comparer logic
class PSComparer : IEqualityComparer[object]
{
    [scriptblock]
    $Comparer

    PSComparer()
    {
        $this.Comparer = {$args[0] -eq $args[1]}
    }

    PSComparer([scriptblock]$comparer)
    {
        $this.Comparer = $comparer
    }

    [bool]
    Equals($a,$b)
    {
        return & $this.Comparer $a $b
    }

    [int]
    GetHashCode($obj)
    {
        if($obj -is [object]){
            return $obj.GetHashCode()
        }
        throw [System.ArgumentNullException]::new('obj')
    }
}

class ConcurrentSet : IDisposable
{
    hidden [ReaderWriterLockSlim]
    $_lock

    hidden [HashSet[object]]
    $_set

    ConcurrentSet()
    {
        # Default to PowerShell comparison logic, ie. `"1" -eq 1`
        $this.Initialize([PSComparer]::new())
    }

    ConcurrentSet([IEqualityComparer[object]]$comparer)
    {
        $this.Initialize($comparer)
    }

    hidden
    Initialize([IEqualityComparer[object]]$comparer)
    {
        $this._set = [HashSet[object]]::new($comparer)
        $this._lock = [System.Threading.ReaderWriterLockSlim]::new()
    }

    [bool]
    Add([object]$item)
    {
        $this._lock.EnterWriteLock()
        try{
            return $this._set.Add($item)
        }
        finally{
            $this._lock.ExitWriteLock()
        }
    }

    [bool]
    Contains([object]$item)
    {
        $this._lock.EnterReadLock()
        try{
            return $this._set.Contains($item)
        }
        finally{
            $this._lock.ExitReadLock()
        }
    }

    [bool]
    Remove([object]$item)
    {
        $this._lock.EnterUpgradeableReadLock()
        try{
            if($this._set.Contains($item)){
                $this._lock.EnterWriteLock()
                try {
                    return $this._set.Remove($item)
                }
                finally {
                    $this._lock.ExitWriteLock()
                }
            }

            return $false
        }
        finally{
            $this._lock.ExitUpgradeableReadLock()
        }
    }

    UnionWith([IEnumerable[object]]$other)
    {
        $this._lock.EnterWriteLock()
        try{
            $this._set.UnionWith($other)
        }
        finally{
            $this._lock.ExitWriteLock()
        }
    }

    IntersectWith([IEnumerable[object]]$other)
    {
        $this._lock.EnterWriteLock()
        try{
            $this._set.IntersectWith($other)
        }
        finally{
            $this._lock.ExitWriteLock()
        }
    }

    ExceptWith([IEnumerable[object]]$other)
    {
        $this._lock.EnterWriteLock()
        try{
            $this._set.ExceptWith($other)
        }
        finally{
            $this._lock.ExitWriteLock()
        }
    }

    SymmetricExceptWith([IEnumerable[object]]$other)
    {
        $this._lock.EnterWriteLock()
        try{
            $this._set.SymmetricExceptWith($other)
        }
        finally{
            $this._lock.ExitWriteLock()
        }
    }

    [bool]
    IsSubsetOf([IEnumerable[object]]$other)
    {
        $this._lock.EnterReadLock()
        try{
            return $this._set.IsSubsetOf($other)
        }
        finally{
            $this._lock.ExitReadLock()
        }
    }

    [bool]
    IsSupersetOf([IEnumerable[object]]$other)
    {
        $this._lock.EnterReadLock()
        try{
            return $this._set.IsSupersetOf($other)
        }
        finally{
            $this._lock.ExitReadLock()
        }
    }

    [bool]
    IsProperSubsetOf([IEnumerable[object]]$other)
    {
        $this._lock.EnterReadLock()
        try{
            return $this._set.IsProperSubsetOf($other)
        }
        finally{
            $this._lock.ExitReadLock()
        }
    }

    [bool]
    IsProperSupersetOf([IEnumerable[object]]$other)
    {
        $this._lock.EnterReadLock()
        try{
            return $this._set.IsProperSupersetOf($other)
        }
        finally{
            $this._lock.ExitReadLock()
        }
    }

    [bool]
    Overlaps([IEnumerable[object]]$other)
    {
        $this._lock.EnterReadLock()
        try{
            return $this._set.Overlaps($other)
        }
        finally{
            $this._lock.ExitReadLock()
        }
    }

    [bool]
    SetEquals([IEnumerable[object]]$other)
    {
        $this._lock.EnterReadLock()
        try{
            return $this._set.SetEquals($other)
        }
        finally{
            $this._lock.ExitReadLock()
        }
    }

    hidden [int]
    get_Count()
    {
        return $this._set.Count
    }

    Dispose()
    {
        if($this._lock -is [System.IDisposable])
        {
            $this._lock.Dispose()
        }
    }
}

0
投票

另一个(丑陋)的方法是

$MySet = [System.Collections.Generic.HashSet[[System.Tuple[string,string]]]]::new()
$Tuple = [System.Tuple[string,string]]::new("Test", "Hello")
[void]$MySet.Add($Tuple)

$MySet

结果:项目1 项目2 长度

项目1 项目2 长度----------------------------------------------------------------------------------------------------------------------测试 你好 2
© www.soinside.com 2019 - 2024. All rights reserved.