Pseudo-Polymorphism in PowerShell

When learning a strongly-typed, object oriented language one of the first concepts you learn is polymorphism. Polymorphic members accept object types that define a particular contract. Class instances that meet this contract, even if they extend from it, can be provided to these members. In PowerShell, this can be true when dealing with .NET classes. For example, our good friend WebClient exposes some polymorphic methods. When you invoke GetWebRequest, it returns a WebRequest class instance. What you’ll notice, via MSDN, is that there are actually more specific versions of a WebRequest. For example, the HttpRequest that is returned when using the WebClient and the HTTP protocol.

The GetWebResponse method accepts a WebRequest rather than any one of the inheritors of the class. This makes it polymorphic.

When using objects as arguments to PowerShell functions, the same polymorphism can be used for .NET types that are already defined but not for objects that are built upon PSCustomObject. Here is how to enforce simple polymorphism in PowerShell functions.

I’ve created a New-Interface function. It is used to define the contract that our new PSCustom objects will be be required to meet.

Since PowerShell can convert between objects based on constructor, the constructor for the new interface type accepts a PSObject. This means that we will be able to directly cast from a PSCustomObject into this particular type. The object also inherits from PSObject so that we can maintain any members that have been defined on the object that we are using to construct an instance of this class. The body of the constructor validates that the input PSObject contains all the correct members. In this case it does not validate Method signatures but could easily be updated to do so. Finally, a TypeAccelerator is used to make casting to this object snappy.

Next, we create our new interface. I’ll create the most widely used polymorphism example: the IAnimal. Implementors of IAnimal will be required to return a Type and expose a Draw property. Both need to be strings.

Now, we can define a polymorphic function that accepts IAnimal. Pretty simple. Since animals are guarenteed to have Draw properties, we can safely call it from within the method.

function Out-Animal([IAnimal]$animal){$animal.Draw
}

Let’s have some fun and put some animals together. I grabbed these ACSII art drawings from Chris.com. We are creating a PSCustomObject just like we would do with any old custom object. The final object we create is a toaster and is not a valid animal because it does not expose the Draw property.