One of the cool features of PowerShell script based Cmdlets is the possibility to define default values for Cmdlet parameters. This is typically done like this:
PARAM ( [Parameter(Mandatory = $false)] [string] $Name = 'my default value' )
Whenever we invoke such a Cmdlet without specifying the Name
parameter the PowerShell runtime will insert the value my default value
into that parameter.
C# based PSCmdlet provide a similar approach to this by providing a PSDefaultValue
attribute to specify a default value:
[Parameter(Mandatory = false)] [PSDefaultValue(Value = 'my default value')] public string Name { get; set; }
However the value of such an annotation will NOT be automatically applied to the Name
property when not specified by the caller.
Of course we can work around this by defining a private backing field:
private string name = 'my default value'; [Parameter(Mandatory = false)] [PSDefaultValue(Value = 'my default value')] public string Name { get { return name; } set { name = value; } }
This certainly does not really improve readability. and of course this is not necessary when using C# 6.0 where you can write it like this:
[Parameter(Mandatory = false)] [PSDefaultValue(Value = 'my default value')] public string Name { get; set; } = 'my default value';
But if you are still using C# 5.0 you can utilise the BeginProcessing
virtual method and apply the default values via reflection
public class PsCmdletBase : PSCmdlet { protected override void BeginProcessing() { base.BeginProcessing(); SetDefaultValues(); } protected virtual void SetDefaultValues() { var propertyInfos = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance) .Where ( propertyInfo => !MyInvocation.BoundParameters.ContainsKey(propertyInfo.Name) && Attribute.GetCustomAttributes(propertyInfo, typeof(ParameterAttribute)).Any() ); foreach (var propertyInfo in propertyInfos) { // only process Parameter with a PSDefaultValue var psDefaultValueAttribute = (PSDefaultValueAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PSDefaultValueAttribute)); if (null == psDefaultValueAttribute) { continue; } propertyInfo.SetValue(this, psDefaultValueAttribute.Value, null); } } }
As you can see we only apply the default values to parameters that are not explicitly specified by the caller (MyInvocation.BoundParameters.ContainsKey
). Now all we have to do is to derive from our new base class and have its default values applied:
public class MyCmdlet : PsCmdletBase { [Parameter(Mandatory = false)] [PSDefaultValue(Value = 'my default value')] public string Name { get; set; } }
Note: it is not possible to process this in a default constructor as the BoundParameters
dictionary is not initialised at that point in time.