Last active
December 10, 2019 06:47
-
-
Save indented-automation/6617c0606a6d761971517b215f401e73 to your computer and use it in GitHub Desktop.
Dynamic enum creation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function New-DynamicModuleBuilder { | |
<# | |
.SYNOPSIS | |
Creates a new assembly and a dynamic module within the current AppDomain. | |
.DESCRIPTION | |
Prepares a System.Reflection.Emit.ModuleBuilder class to allow construction of dynamic types. The ModuleBuilder is created to allow the creation of multiple types under a single assembly. | |
.EXAMPLE | |
New-DynamicModuleBuilder | |
#> | |
[CmdletBinding()] | |
[OutputType([System.Reflection.Emit.ModuleBuilder])] | |
param ( | |
# A name for the in-memory assembly. | |
[System.Reflection.AssemblyName]$AssemblyName = (New-Guid).ToString(), | |
# By default, this function stores a ModuleBuilder in a global variable called DefaultDynamicAssembly. The ModuleBuilder object is available for New-Enum without needing explicit assignment. | |
[String]$DynamicAssemblyVariable = 'DefaultDynamicAssembly' | |
) | |
try { | |
if (Test-Path Variable:\$DynamicAssemblyVariable) { | |
return Get-Variable $DynamicAssemblyVariable -ValueOnly | |
} | |
$appDomain = [AppDomain]::CurrentDomain | |
# Create a dynamic assembly in the current AppDomain | |
$assemblyBuilder = $appDomain.DefineDynamicAssembly( | |
$AssemblyName, | |
[System.Reflection.Emit.AssemblyBuilderAccess]::Run | |
) | |
$moduleBuilder = $assemblyBuilder.DefineDynamicModule($AssemblyName.Name) | |
# Create a transient dynamic module within the new assembly | |
New-Variable $DynamicAssemblyVariable -Scope Global -Value $moduleBuilder | |
return $moduleBuilder | |
} catch { | |
$pscmdlet.ThrowTerminatingError($_) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function New-Enum { | |
<# | |
.SYNOPSIS | |
Creates a new enum (System.Enum) from a hashtable using an existing instance of ModuleBuilder. | |
.DESCRIPTION | |
New-Enum dynamically creates an enum with the specified name (and namespace). | |
A hashtable is used to populate the enum. All values passed in via the hashtable must be able to convert to the enum type. | |
The enum is created, but not returned by this function. | |
.EXAMPLE | |
PS> New-DynamicModuleBuilder "Example" | |
PS> $EnumMembers = @{Cat = 1; Dog = 2; Tortoise = 4; Rabbit = 8} | |
PS> New-Enum -Name "Example.Pets" -Flags -Members $EnumMembers | |
PS> [Example.Pets]10 | |
Creates a new enumeration in memory, then returns values "dog" and "rabbit". | |
.EXAMPLE | |
PS> New-DynamicModuleBuilder "Example" -UseGlobalVariable $False | |
PS> New-Enum -ModuleBuilder $Builder -Name "Example.Byte" -Type "Byte" -Members @{ one = 1; two = 2 } | |
PS> [Example.Byte]2 | |
Uses a user-defined variable to store the created dynamic module. The example returns the value "two". | |
.EXAMPLE | |
PS> New-Enum -Name "Example.NumbersLow" -Members @{One=1; Two=2} | |
PS> New-Enum -Name "Example.NumbersHigh" -Members @{OneHundred=100; TwoHundred=200} | |
PS> [UInt32][Example.NumbersLow]::One + [UInt32][Example.NumbersHigh]::OneHundred | |
Multiple Enumerations can be built within the same dynamic assembly, a module builder only needs to be created once. | |
#> | |
[CmdletBinding()] | |
param ( | |
# A name for the enum, a namespace may be included. | |
[Parameter(Mandatory = $true, Position = 1)] | |
[ValidatePattern('^(\w+.)*\w+$')] | |
[ValidateScript( { $true; if ($_ -as [Type]) { throw 'The requested type already exists.' } } )] | |
[String]$Name, | |
# A .NET value type, by default Int32 is used. The type name is passed as a string and converted to a Type by the function. | |
[Type]$Type = 'Int32', | |
# Optionally sets the System.FlagsAttribute on the enum, indicating the enum can be treated as a bit field. Note that the enum members must support this attribute. | |
[Switch]$Flags, | |
# A hashtable describing the members of the enum. | |
[Parameter(Mandatory = $true)] | |
[HashTable]$Members, | |
# A dynamic module within a dynamic assembly, created by New-DynamicModuleBuilder. | |
[String]$DynamicAssemblyVariable = 'DefaultDynamicAssembly' | |
) | |
try { | |
if ($psboundparameters.ContainsKey('DynamicAssemblyVariable')) { | |
if (Test-Path Variable:\$DynamicAssemblyVariable) { | |
$moduleBuilder = Get-Variable $DynamicAssemblyVariable -ValueOnly | |
} | |
if (-not $moduleBuilder -or $moduleBuilder -isnot [System.Reflection.Emit.ModuleBuilder]) { | |
throw 'The dynamic assembly variable does not exist or does not contain a ModuleBuilder object.' | |
} | |
} else { | |
$moduleBuilder = New-DynamicModuleBuilder | |
} | |
$enumBuilder = $moduleBuilder.DefineEnum( | |
$Name, | |
[System.Reflection.TypeAttributes]::Public, | |
$Type | |
) | |
if ($Flags) { | |
$enumBuilder.SetCustomAttribute( | |
[FlagsAttribute].GetConstructor([Type]::EmptyTypes), | |
@() | |
) | |
} | |
foreach ($key in $Members.Keys) { | |
$enumBuilder.DefineLiteral($key, [Convert]::ChangeType($Members[$key], $Type)) | Out-Null | |
} | |
$null = $enumBuilder.CreateType() | |
} catch { | |
$pscmdlet.ThrowTerminatingError($_) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment