Question

Quick background: I am fairly new to PowerShell but am a well-versed C# dev. I have mixed feelings about PowerShell so far. I love it one day, hate it the next. Just when I think I've got it figured out, I get stumped for hours trial-and-error-ing some feature I think it should implement but doesn't.

I would like PowerShell to let me override an object's ToString() method (which it does) such that when an object is referenced inside a double-quoted string, it will call my custom ToString method (which it does not).

Example:

PS C:\> [System.Text.Encoding] | Update-TypeData -Force -MemberType ScriptMethod -MemberName ToString -Value { $this.WebName }

PS C:\> $enc = [System.Text.Encoding]::ASCII

PS C:\> $enc.ToString()  ### This works as expected

us-ascii

PS C:\> "$enc"  ### This calls the object's original ToString() method. 

System.Text.ASCIIEncoding

How does one define the variable expansion behavior of a .NET object?

Was it helpful?

Solution 2

I stumbled upon a semi-solution, and discovered yet another strange behavior in Powershell. It appears that when string expansion involves a collection, the user-defined ScriptMethod definition of the ToString() method is called, as opposed to the object's original ToString() method.

So, using the above example, all that is necessary is adding one lousy comma:

PS C:\> [System.Text.Encoding] | Update-TypeData -Force -MemberType ScriptMethod -MemberName ToString -Value { $this.WebName }

PS C:\> $enc = ,[System.Text.Encoding]::ASCII # The unary comma operator creates an array

PS C:\> "$enc"  ### This now works

us-ascii

This seems like a bug to me.

OTHER TIPS

The language specification doesn't say anything about overriding variable substitution, so I don't think there's a defined way of doing exactly what you want. If a single class was involved you could subclass it in C#, but for a class hierarchy the nearest I can think of is to produce a wrapper around the object which does have your desired behaviour.

$source = @"
using System.Text;
public class MyEncoding
{
    public System.Text.Encoding encoding;
    public MyEncoding()
    {
        this.encoding = System.Text.Encoding.ASCII;
    }

    public MyEncoding(System.Text.Encoding enc)
    {
        this.encoding = enc;
    }
    public override string ToString() { return this.encoding.WebName; }
}
"@
Add-Type -TypeDefinition $Source

Then you can use it like this:

PS C:\scripts> $enc = [MyEncoding][System.Text.Encoding]::ASCII;

PS C:\scripts> "$enc"
us-ascii

PS C:\scripts> $enc = [MyEncoding][System.Text.Encoding]::UTF8;

PS C:\scripts> "$enc"
utf-8

PS C:\scripts> $enc.encoding


BodyName          : utf-8
EncodingName      : Unicode (UTF-8)
HeaderName        : utf-8
WebName           : utf-8
WindowsCodePage   : 1200
IsBrowserDisplay  : True
IsBrowserSave     : True
IsMailNewsDisplay : True
IsMailNewsSave    : True
IsSingleByte      : False
EncoderFallback   : System.Text.EncoderReplacementFallback
DecoderFallback   : System.Text.DecoderReplacementFallback
IsReadOnly        : True
CodePage          : 65001

If you don't like the extra .encoding to get at the underlying object you could just add the desired properties to the wrapper.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top