Как правильно создать и изменить хэш-набор кортежей в powershell? - PullRequest
1 голос
/ 23 апреля 2020

Я пытаюсь создать хэш-набор из двухэлементных (парных) кортежей в PowerShell таким образом:

$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".

Как правильно go об этом правильно?

Бонус: Нет поточно-безопасной версии из hashset - есть ли способ изменить / прочитать hashsets потокобезопасным образом?

Ответы [ 2 ]

1 голос
/ 23 апреля 2020

Проблема здесь в том, что [System.Tuple]::Create($a,$b) не на самом деле создает экземпляр [System.Tuple], но [System.Tuple[T1,T2]], где T1 - это тип $a, а T2 это тип $b.

Несмотря на то, что они разделяют часть имени, [System.Tuple[T1,T2]] - это , который нельзя назначить на [System.Tuple], поэтому нам нужно найти другой аргумент типа для ваш [HashSet].

Поскольку значения вашего элемента кортежа являются строками, go с [System.Tuple[string,string]]:

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

ОТВЕТ НА БОНУС

Если вы go в другом Направление и просто создайте 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 голосов
/ 23 апреля 2020

Другой ( некрасивый ) подход -

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

$MySet

Результат:

Item1 Item2 Length
----- ----- ------
Test  Hello      2
...