Scott Hanselman described how to “spot-weld” new properties on existing types using the types.ps1xml file. When writing your own Cmdlets this might not be the best way to modify the objects you return. In this post I’m going into how to change existing types in code using ETS Properties based on a SharpSvn/Subversion cmdlet. A common use-case would be to fix a function that returns a string instead of a FileInfo or Uri instance (that can be passed along the pipeline).
The main trick is not to return the plain object, but a PSObject wrapping the plain object. The PSObject allows you to add and remove custom properties and methods from C# code.
This is the code before overriding propeties and extending the pipeline object
protected override void ProcessRecord()
{
Client.Info(
GetTarget<SvnTarget>(),
SvnArguments,
delegate(object sender, SvnInfoEventArgs e)
{
e.Detach();
WriteObject(e);
});
}
And its output.
Get-SvnInfo | % { $_.Path.GetType() }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
SvnInfoEventArgs has a Path property of type String. For a Cmdlet it’s more usual to write FileInfo objects to the pipeline, so we need to remove the original Path property, and provide a new one.
The properties/methods that seem to make most sense here are:
- CodeProperty/CodeMethod
Allows you to write C# code behind a new property or method, this is the most flexible, and can only be added from C# code. In PowerShell you would write a ScriptProperty or ScriptMethod instead, implementing it in PowerShell. - AliasProperty
Exposes an existing property with another name, nice for creating a consistent PowerShell API on top of a non-existing API - NoteProperty
This is just a readonly Key/Value
Adding a new property with the same name overrides the existing one
protected override void ProcessRecord()
{
Client.Info(
GetTarget<SvnTarget>(),
SvnArguments,
delegate(object sender, SvnInfoEventArgs e)
{
e.Detach();
PSObject obj = new PSObject(e);
obj.Properties.Add(new PSCodeProperty(
"Path",
GetType().GetMethod("GetPath")));
WriteObject(obj);
});
}
public static FileInfo GetPath(PSObject obj)
{
SvnInfoEventArgs e = obj.BaseObject as SvnInfoEventArgs;
if (e == null)
return null;
return new FileInfo(e.Path);
}
The method represented by MethodInfo has to be public, static, non-void and take one parameter of type PSObject to work.
This overrides the Path property:
Get-SvnInfo | % { $_.Path.GetType() }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True FileInfo System.IO.FileSys...
If you have “spot-welded” on extra properties on FileInfo, those will also be present on $_.Path ofcourse.