Skip to content

Instantly share code, notes, and snippets.

@indented-automation
Last active December 10, 2019 06:47
Show Gist options
  • Save indented-automation/6617c0606a6d761971517b215f401e73 to your computer and use it in GitHub Desktop.
Save indented-automation/6617c0606a6d761971517b215f401e73 to your computer and use it in GitHub Desktop.
Dynamic enum creation
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($_)
}
}
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