Skip to content

Instantly share code, notes, and snippets.

@jborean93
Created July 1, 2026 05:41
Show Gist options
  • Select an option

  • Save jborean93/f5817a480e2e461152eedec77aff6a7b to your computer and use it in GitHub Desktop.

Select an option

Save jborean93/f5817a480e2e461152eedec77aff6a7b to your computer and use it in GitHub Desktop.
A POC to illustrate how a binary cmdlet could change how cmdlets are defined and written in C#
using System;
using System.Management.Automation;
using System.Threading;
using System.Threading.Tasks;
using Jborean.PwshAsync.Generators;
namespace SampleCmdlet.Examples;
[AsyncPSCmdlet(VerbsDiagnostic.Test, "SimpleNoOutput")]
public partial class SimpleCmdletNoOutput
{
// Parameter sets are defined by a nested record type with the
// [ParameterSet] attribute. All record properties are parameters
// for that parameter set. The record name is the parameter set name.
// OutputType is defined by the AsyncPipeline<T> (or no T for no output)
// in the *Async method signature.
[ParameterSet]
internal record Params(
[Parameter(Mandatory)]
string Message, // Parameter with custom [Parameter()] logic
string? Prefix, // Just [Parameter()] explicitly
int Count = 1 // Same but with default value
);
protected async Task EndAsync(
Params p,
AsyncPipeline pipeline, // Non-generic, no output allowed
CancellationToken cancellationToken)
{
await Task.Delay(10, cancellationToken);
pipeline.WriteVerbose("Verbose and other streams still work");
// Writing output is impossible because the pipeline type is non-generic.
// This won't work.
// await pipeline.WriteAsync("test")
}
}
[AsyncPSCmdlet(VerbsCommon.Get, "Greeting")]
public partial class SimpleCmdletWithOutput
{
[ParameterSet]
internal record Params(string? Name);
protected async Task ProcessAsync(
Params p,
AsyncPipeline<string> pipeline, // AsyncPipeline<T> defines OutputType
CancellationToken cancellationToken)
{
await Task.Delay(10, cancellationToken);
await pipeline.WriteAsync($"Hello, {p.Name ?? "World"}!", cancellationToken);
}
}
// Default parameter set is defined in the cmdlet attribute like usual.
[AsyncPSCmdlet(VerbsCommon.Get, "User", DefaultParameterSetName = "ByName")]
public partial class MultipleParamSetsCmdlet
{
[ParameterSet]
internal record ByName(
[Parameter(Position = 0)]
string Name,
SwitchParameter Detailed);
[ParameterSet]
internal record ById(
[Parameter(Position = 0)]
[ValidateRange(1, int.MaxValue)] // parameters can have normal validation attributes
int Id,
SwitchParameter Detailed);
// Cmdlets can have their own fields to persist state between Begin/Process/End
// blocks.
private int UserId { get; set; }
// Runs under the ByName parameter set, does not need to be defined
// for all parameter sets but there has to be at least 1 block per
// parameter set.
protected async Task BeginAsync(
ByName p,
AsyncPipeline<string> pipeline,
CancellationToken cancellationToken)
{
UserId = await LookupUserIdByNameAsync(p.Name, cancellationToken);
}
// End block for ByName parameter set
protected async Task EndAsync(
ByName p,
AsyncPipeline<string> pipeline,
CancellationToken cancellationToken)
{
if (UserId == 0)
{
// Begin failed to find user, skip this.
return;
}
string result = await LookupUserInfoById(UserId, p.Detailed.IsPresent, cancellationToken);
await pipeline.WriteAsync(result, cancellationToken);
}
// End block for ById parameter set
protected async Task EndAsync(
ById p,
AsyncPipeline<string> pipeline,
CancellationToken cancellationToken)
{
string result = await LookupUserInfoById(p.Id, p.Detailed.IsPresent, cancellationToken);
await pipeline.WriteAsync(result, cancellationToken);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment