Skip to content

Instantly share code, notes, and snippets.

@santisq
Last active May 26, 2026 11:09
Show Gist options
  • Select an option

  • Save santisq/0dd876d5fd868cc0194dc27e9cb3b089 to your computer and use it in GitHub Desktop.

Select an option

Save santisq/0dd876d5fd868cc0194dc27e9cb3b089 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
public sealed class MyCustomClass
{
// Ideally this should be a hardcoded set of Methods or Properties that can be set on this object
private static readonly HashSet<string> _selfMethods = new(
typeof(MyCustomClass).GetMethods().Select(e => e.Name),
StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, object?> _properties = new(StringComparer.OrdinalIgnoreCase);
public object? this[string property]
{
get => _properties.TryGetValue(property, out object? value) ? value : null;
set => _properties[property] = value;
}
internal Collection<PSAdaptedProperty> Properties
{
get => new([.. _properties.Select(kv => new PSAdaptedProperty(kv.Key, kv.Value))]);
}
// Check if the property passed to the adapter is actually a method, if so,
// GetProperty has to return null so the adapter falls back to calling it
internal static bool IsMethod(string property) => _selfMethods.Contains(property);
}
public sealed class MyPropertyAdapter : PSPropertyAdapter
{
public override Collection<PSAdaptedProperty>? GetProperties(object baseObject)
=> baseObject is MyCustomClass customClass
? customClass.Properties : null;
public override PSAdaptedProperty? GetProperty(object baseObject, string propertyName)
=> baseObject is MyCustomClass customClass && !MyCustomClass.IsMethod(propertyName)
? new(propertyName, customClass[propertyName]) : null;
public override string? GetPropertyTypeName(PSAdaptedProperty adaptedProperty)
=> adaptedProperty.Tag?.GetType().Name;
public override object? GetPropertyValue(PSAdaptedProperty adaptedProperty)
=> adaptedProperty.Tag;
// Should always be True, makes no sense to have Property { set; } only
public override bool IsGettable(PSAdaptedProperty adaptedProperty) => true;
// Should be defined by the implementer, in this case, any dynamic property is settable
public override bool IsSettable(PSAdaptedProperty adaptedProperty) => true;
public override void SetPropertyValue(PSAdaptedProperty adaptedProperty, object value)
{
if (adaptedProperty.BaseObject is MyCustomClass customClass)
customClass[adaptedProperty.Name] = value;
}
}

Compile and load the type... this could be pre-compiled e.g. via dotnet CLI then the type loaded via Assembly.LoadFrom(...) or Add-Type or Import-Module if part of an actual PowerShell Module.

Once loaded you can Update-TypeData -TypeAdapter like shown in the example:

Add-Type -Path .\adapterExample.cs -WA 0 -IgnoreWarnings
Update-TypeData -TypeAdapter ([MyPropertyAdapter]) -TypeName ([MyCustomClass])

If the adapter is part of the a module you can have a ...Types.ps1xml instead and loaded via TypesToProcess:

<?xml version="1.0" encoding="utf-8"?>
<Types>
  <Type>
    <Name>MyCustomClass</Name>
    <TypeAdapter>
      <TypeName>MyPropertyAdapter</TypeName>
    </TypeAdapter>
  </Type>
</Types>

Then the actual usage, pretty much like an AD Object... it is actually ADEntityAdapter the one in charge of this...

$class = [MyCustomClass]::new()
$class.Id = [guid]::NewGuid()
$class.Name = 'john.galt'
$class

# Id                                   Name
# --                                   ----
# bbd94707-59b8-4de8-9624-89bfa2ca6700 john.galt

$class.psobject.Properties

# BaseObject      : MyCustomClass
# Tag             : bbd94707-59b8-4de8-9624-89bfa2ca6700
# MemberType      : Property
# Value           : bbd94707-59b8-4de8-9624-89bfa2ca6700
# IsSettable      : True
# IsGettable      : True
# TypeNameOfValue : Guid
# Name            : Id
# IsInstance      : True
#
# BaseObject      : MyCustomClass
# Tag             : john.galt
# MemberType      : Property
# Value           : john.galt
# IsSettable      : True
# IsGettable      : True
# TypeNameOfValue : String
# Name            : Name
# IsInstance      : True
@santisq
Copy link
Copy Markdown
Author

santisq commented May 26, 2026

@mklement0 apparently we can't return new PSAdaptedProperty(propertyName, customClass[propertyName]) always, it has to return null when it's a method so the adapter falls back to calling it. I added a comment to the code but having a fully dynamic object is clearly not the best idea. Probably the implementer should decide on a list of properties that can be set on the object instead of self reflection like I did there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment