3

Is there any way to modify a string without loosing any note properties it has?

$t = 'something'|Add-Member noteproperty one 1 -PassThru
$t.one
1
$t = 'else'
$t.one
<nothing here>
$t.value = 'else'  # The property 'value' cannot be found on this object
bielawski
  • 1,466
  • 15
  • 20

1 Answers1

6

In short: No, you cannot modify the value of a [string] variable without losing any instance-level NoteProperty members attached to the previous value.


The NoteProperty instance is associated with the specific object (string) instance you're piping to Add-Member.[1]

By assigning a different string instance to variable $t ($t = 'else'), the original instance is lost, and along with it the NoteProperty you've added.

Also note that a [string] instance, even if decorated with NoteProperty members, never has a .value property. However, cmdlets such as ConvertTo-Json may give the appearance that there's a .value property, because that's how they serialize a string or .NET primitive type with NoteProperty members.


Generally, it's best to avoid adding NoteProperty members to strings and instances of .NET value types, for two reasons:

  • You cannot modify such instances themselves, you can only replace them with modified copies (stored in the same variable), and these copies invariably do not have the original's NoteProperty members.

  • Even without modification, passing NoteProperty-decorated strings or value types as parameter values results in loss of instance-level NoteProperty members under the following conditions:

    • If the target parameter is declared as a specific input type and the input type is either a string ([string]) or a .NET primitive type, which comprises the following value types: [Bool], [Byte], [SByte], [Int16], [UInt16], [Int32], [UInt32], [Int64], [UInt64], [IntPtr], [UIntPtr], [Char], [Double], [Single].

    • Additionally - and this applies to all value-type instances as well as [string] - if the target parameter is declared as an array of the specific input type (e.g., if a [datetime[]]-typed parameter receives a decorated [datetime] instance)

    • However, passing such instances to untyped or - as is effectively the same - [object]- or [object[]]-typed parameters does preserve the decorations.


[1] Technically, for [string] instances only, it is an invisible [psobject] wrapper that the instance-level NoteProperty is associated with - if the [string] instance itself were used, problems could arise due to the .NET CLRs string-interning, where seemingly separate strings with the same content can point to the very same instance as an optimization - and an instance-specific NoteProperty shouldn't unexpectedly surface on what the user expects to be a different string.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Yes, and it is also lost when using `[string].Replace`. – Dávid Laczkó Dec 12 '19 at 22:06
  • In fact they are lost if you pass the string to a function that decorates the parameter with [string] or [string[]] too. Pretty much everything seems to discard them. I just hoped there was some trick I'd not heard of. – bielawski Dec 12 '19 at 22:33
  • Good point, @DávidLaczkó; more generally, given that strings are _immutable_, any decoration is lost whenever a _modified copy_ is created - please see my update. – mklement0 Dec 13 '19 at 17:21
  • 1
    Exactly, as setting the variable to the modified copy is an assignment. Nice piece of work! My opinion is that this NoteProperty is not a good idea at all, too many things to consider. I would use a custom object instead. – Dávid Laczkó Dec 13 '19 at 19:21