Danny
Danny

Reputation: 410

Powershell GetType

I want to check is some type exist. For example:

Add-Type -TypeDefinition '
public class Test{
    public static int aaa(){
        return 1;
    }
}'

[test]::aaa() # 1

[type]::GetType('test') # empty

but GetType() not works for my 'test' type.

Upvotes: 2

Views: 1303

Answers (2)

mklement0
mklement0

Reputation: 437743

To complement Theo's helpful answer, which undoubtedly shows the best solution (-as [Type]), with why [Type]::GetType('Test') failed:

  • System.Type.GetType() only finds types from assemblies that were loaded from or saved to disk - that is, types either loaded from preexisting assembly files or types in dynamically created assemblies also, optionally, persisted to disk.

  • By contrast, when you define types dynamically in PowerShell, they are created in dynamic, in-memory assemblies that are not persisted to disk, so [Type]::GetType() doesn't see them. Note that this applies equally to:

    • Types created with Add-Type -TypeDefinition or -MemberDefinition

    • Types created with the (PSv5+) class and enum keywords, i.e. custom PowerShell classes and enumerations.


However, [Type]::GetType() offers overloads that enable hooking custom assembly-resolver functionality into the type-lookup process, i.e. a way to look for types in assemblies in additional places.

This is presumably what PowerShell uses behind the scenes to include its dynamic in-memory-only assemblies in the lookup process, which comes into play in the following contexts (case-insensitively, as PowerShell generally is):

  • In the most obvious form, in type literals ([<typeName>]), so that you can refer to your Test type as [Test], for instance.

    • Such type literals therefore also work with -is, the type(-inheritance) / interface test operator, so you can test a given object for being of or deriving from the given type / implementing the given interface type.

    • Caveat: If the type name inside [...] isn't recognized as a loaded or dynamically declared type, an exception occurs, which surfaces as a statement-terminating error in PowerShell.

  • In the context of -as, the conditional type conversion operator, as shown in Theo's answer ('<typeName>' -as [type]), where [type] refers again to System.Type.

    • This approach tests if the string contains a type name that refers to a loaded type that can therefore be converted to a type-information object.
    • If so, such a type-information object is returned (the equivalent of [Test], in your example); if not, $null is returned
    • In a Boolean context such as an if conditional, a type-information object getting returned is implicitly $true, where as $null is implicitly $false - see the bottom section of this answer for the exact rules of implicit to-Boolean conversion), so the -as [type] approach is the most convenient way to test a type's presence.
  • When you cast a type-name string to [type]: [type] 'Test'

    • Unlike the -as approach, this approach triggers an exception that surfaces as a statement-terminating error in PowerShell.
    • The (more verbose and less efficient) equivalent of if ('Test' -as [type]) ... is
      if ($(try { [type] 'Test' } catch { $null })) ...

A note re namespaces:

  • Types created with class and enum have no namespace component, so it is sufficient to refer to the types by their own name alone, such as [Test] in the case at hand.

  • Types created via Add-Type:

    • via -TypeDefinition (arbitrary C# code):

      • Whether you need a namespace qualifier depends on whether you enclose the new type's declaration in a namespace <identifier> { ... } construct or not.
    • via -MemberDefinition (C# code for declaring static methods, typically for P/Invoke calls):

      • You do need a namespace qualifier to refer to the resulting type, namely either the one you've specified explicitly via the -Namespace parameter or the the implicitly used Microsoft.PowerShell.Commands.AddType.AutoGeneratedTypes.WinApi namespace.

      • You can bypass having to refer to the type by namespace-qualified name by using the -PassThru switch and saving the type-definition object in a variable, which allows you to call its static methods directly via that variable.

Upvotes: 3

Theo
Theo

Reputation: 61068

You can test if the type already exists with

if (-not ('Test' -as [type])) {
# or 
# if (-not ([System.Management.Automation.PSTypeName]'Test').Type) {
    Add-Type -TypeDefinition '
    public class Test{
        public static int aaa(){
            return 1;
        }
    }'
}

I don't know why [type]::GetType('test') doesn't work though, but

("Test" -as [type])

does work and returns either $null or

IsPublic IsSerial Name                                     BaseType                                                                                                               
-------- -------- ----                                     --------                                                                                                               
True     False    Test                                     System.Object

Upvotes: 3

Related Questions