Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch on bitwise enumeration

Tags:

powershell

I have an function that fetches data, validates it against a set of user specified parameters and outputs if it is correct or not and based on that takes action, and it works just fine. As a further development of the function I've tried to move to a bitwise enumeration for the 'flags' but cannot get it to work like i want.

I want to switch through the enumeration and run the case for all possible 'matches' but i only get matches on the case when there is only a direct match on the value. Ie. when the value is 1 it runs the case 'Milk' but if the value is 3 it will not run the 'Milk' and the 'Bread' case ... i kinda assume its trying to run the 'Milk, Bread' case.

Is there any easy way that I've failed to see around this? ><

[Flags()] enum Breakfast {

    Nothing = 0
    Milk = 1
    Bread = 2
    Egg = 4
    Bacon = 8
}

$BreakfastFlag = 3


Switch ([Breakfast]$BreakfastFlag){

    Nothing {
    Write-Host "No breakfast was selected" -ForegroundColor White -BackgroundColor Red
    } 

    Milk {
    Write-Host "Milk was selected!" -ForegroundColor White -BackgroundColor DarkGreen
    }

    Bread {
    Write-Host "Bread was selected!" -ForegroundColor White -BackgroundColor DarkGreen
    }

    Egg {
    Write-Host "Egg was selected!" -ForegroundColor White -BackgroundColor DarkGreen
    }

    Bacon {
    Write-Host "Bacon was selected!" -ForegroundColor White -BackgroundColor DarkGreen
    }

}
like image 555
Jonas Avatar asked Jan 05 '23 11:01

Jonas


2 Answers

Yet another variant:

[Flags()] enum Breakfast {
    Nothing = 0
    Milk = 1
    Bread = 2
    Egg = 4
    Bacon = 8
}

$BreakfastFlag = 3

Switch ([Breakfast]$BreakfastFlag)
{
    Nothing {
        Write-Host "No breakfast was selected" -ForegroundColor White -BackgroundColor Red
    }

    { $_ -band [Breakfast]::Milk } {
        Write-Host "Milk was selected!" -ForegroundColor White -BackgroundColor DarkGreen
    }

    { $_ -band [Breakfast]::Bread } {
        Write-Host "Bread was selected!" -ForegroundColor White -BackgroundColor DarkGreen
    }

    { $_ -band [Breakfast]::Egg } {
        Write-Host "Egg was selected!" -ForegroundColor White -BackgroundColor DarkGreen
    }

    { $_ -band [Breakfast]::Bacon } {
        Write-Host "Bacon was selected!" -ForegroundColor White -BackgroundColor DarkGreen
    }
}

Output:

Milk was selected!
Bread was selected!
>> Script Ended
like image 119
Anton Krouglov Avatar answered Jan 14 '23 07:01

Anton Krouglov


If brevity is the goal, make an auxiliary function that accepts a hashtable with keys as text names of the flags and values as anything, including a scriptblock:

function Switch-Flag($value, [hashtable]$switchboard) {
    $type = $value.GetType()
    $intValue = $value.value__
    $parsed = New-Object $type
    foreach ($e in $switchboard.GetEnumerator()) {
        $key = $e.name
        if ($key -is $type) {
            $r = $key.value__
        } elseif ($key -is [int]) {
            $r = $key
        } elseif ($type::TryParse($key, [ref]$parsed)) {
            $r = $parsed.value__
        } else {
            continue
        }
        if (($intValue -band $r) -eq $r) {
            if ($e.value -is [ScriptBlock]) {
                $e.value.Invoke()
            } else {
                $e.value
            }
        }
    }
}

Usage example 1, simplified:

[Breakfast]$flag = 3
Switch-Flag $flag @{
    Milk =  { Write-Host liquid }
    Bread = { Write-Host crunchy }
}

Usage example 2, ensuring the order of choices:

$flag = 3
Switch-Flag ([Breakfast]$flag) ([ordered]@{
    Milk =  'liquid'
    Bread = 'crunchy'
})

Usage example 3, using enum as keys:

[Breakfast]$flag = 3
Switch-Flag $flag @{
    ([Breakfast]::Milk) =  { $milk++ }
    ([Breakfast]::Bread) = { $bread++ }
}

P.S. This is 10 times slower than using -band expressions shown in the other answer (180 microseconds vs 18 microseconds) because apparently PowerShell is smart enough to cache the expressions and it also runs the expression scriptblocks in local scope, that is, if I understand correctly, it doesn't create a new scope in case of switch expressions.

like image 25
wOxxOm Avatar answered Jan 14 '23 06:01

wOxxOm