Powershell: Could PSObject be made easier to work with in C#?

Created on 2 Sep 2018  路  6Comments  路  Source: PowerShell/PowerShell

I've recently been venturing into C# to write PowerShell Cmdlets and found that PSObject was quite cumbersome to work with compared to how you can work with them in PowerShell. Instead of being able to just assign properties, you need to always use PSObject.Properties. Especially when having variables that could be _either_ PSObject or .NET types it is very cumbersome to access properties.

C# has the dynamic type, which gives an experience of dynamic objects like PSObjects in PowerShell that can be expanded at runtime. It seems to work under the hood through the ExpandoObject class, which implements the IDynamicMetaObjectProvider interface. Could PSObject implement this too, to allow easy manipulation of its dynamic properties?

Issue-Question Resolution-Answered WG-Engine

Most helpful comment

Hi @felixfbecker PSObject has implemented IDynamicMetaObjectProvider since PowerShell Version 3 when it was rehosted on the DLR. See:

src\System.Management.Automation\engine\MshObject.cs:47: public class PSObject : IFormattable, IComparable, ISerializable, IDynamicMetaObjectProvider

You should be able to use dynamic instead of PSObject in most places in your code.

All 6 comments

Hi @felixfbecker PSObject has implemented IDynamicMetaObjectProvider since PowerShell Version 3 when it was rehosted on the DLR. See:

src\System.Management.Automation\engine\MshObject.cs:47: public class PSObject : IFormattable, IComparable, ISerializable, IDynamicMetaObjectProvider

You should be able to use dynamic instead of PSObject in most places in your code.

Indeed. I have been able to use the dynamic keyword with PSObjects in C# code.

You're right, I was just struggling with reflection on it. Works as expected!

Actually, I think I spoke too soon. It seems to be possible to _read_ properties from it as a dynamic object, but not possible to set _new_ properties on it as you can with ExpandoObject for example. I would expect to be able to set a new property on PSObjects, which would create a new NoteProperty if the property does not exist:

dynamic obj = new PSObject(); // could be pipeline input etc
obj.Annotations = 123;

Instead, it errors:

System.Management.Automation.SetValueInvocationException: Exception setting "Annotations": "The property 'Annotations' cannot be found on this object. Verify that the property exists and can be set." ---> System.Management.Automation.RuntimeException: The property 'Annotations' cannot be found on this object. Verify that the property exists and can be set.
   at System.Management.Automation.Language.PSSetMemberBinder.SetAdaptedValue(Object obj, String member, Object value)
   --- End of inner exception stack trace ---
   at System.Management.Automation.ExceptionHandlingOps.ConvertToMethodInvocationException(Exception exception, Type typeToThrow, String methodName, Int32 numArgs, MemberInfo memberInfo)
   at System.Management.Automation.Language.PSSetMemberBinder.SetAdaptedValue(Object obj, String member, Object value)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)

@felixfbecker - there is no simple syntax for adding properties to PSObject - not in C# nor in PowerShell. This is intentional and it helps avoid errors.

If you really do want ExpandoObject semantics, then just use ExpandoObject. I'm pretty sure you can even wrap an ExpandoObject in a PSObject and it will work as you'd expect.

Oh interesting, I wasn't aware of that. I always thought objects in PowerShell were like in JavaScript.

The use case I was dealing here with was I accepted PSObject _or_ strong .NET objects, and allowed the PSObject to be partial (missing properties that would just use the default value). I then had to set a nested properties on an input object, which could be either a PSObject or a .NET object. The .NET input would already have the property defined of course (even if not set), but a custom PSObject may have it not set. Maybe I felt the pain in C# more because in PowerShell, you can always use Add-Member to add a property to _any_ object, while in C#, I need to use assignment for .NET types, but PSObject.Properties.Add() for PSObject.

I solved this at the moment with an extension method on object SetDynamicPropertyValue() that does this type switch and will add a NoteProperty if needed, which works fine, it's just a bit cumbersome. I think I just got overly excited that I can replace these extension methods with dynamic everywhere, but turns out I still need them for some aspects (adding properties).

Was this page helpful?
0 / 5 - 0 ratings