views:

35

answers:

1

I have been implementing makeshift types by dynamically adding methods to PSObjects

I want to be able to compare two instances of my objects using the "-eq" "-lt" and "-gt" operators (I assume this would require me to implement interfaces like IComparible, and IEquatible)

Is this sort of thing possible? (I'm thinking perhaps not as these things usually happen on the type level and my makeshift "types" are all the same type)

If not is there something else I can do (other than using c#)?

+3  A: 

I don't think you can tack on the interfaces to the psobject you're creating. However, you can create your type in a C# class that implements the appropriate interfaces and overrides the appropriate methods. You can even define this type in a here string and use Add-Type to compile it and load it into PowerShell. Then you just create that type instead of psobject. It should have all the relevant properties you're interested as well as the ability to do comparison/equality.

Per my comment, here is how to do this in your script with minimal headache bewteen executions i.e. you only need to change the typename variable whenever you change the C# code.

$foo = "Foo"

$src = @"
using System;
public class $foo : IComparable {
    public string Name {get; set;}
    public int Age {get; set;}
    public int CompareTo(object obj)
    {
        $foo other = obj as $foo;
        if (other == null) 
            throw new ArgumentException("arg must be type $foo or not null");
        return Age.CompareTo(other.Age);
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        if (Object.ReferenceEquals(this, obj)) return true;

        $foo other = obj as $foo;
        if (other == null) 
            throw new ArgumentException("arg must be type $foo or not null");
        return Age.Equals(other.Age) && Name.Equals(other.Name);    
    }

    public override int GetHashCode()
    {
        return Age.GetHashCode() ^ Name.GetHashCode();
    }

    public override string ToString()
    {
        return String.Format("Name: {0}, age: {1}", Name, Age);
    }
}
"@

if (![Type]::GetType($foo))
{
    Add-Type -TypeDefinition $src -Language CSharpVersion3
}

$foo1 = New-Object $foo
$foo1.Age = 47
$foo1.Name = 'Keith'
$foo1


$foo2 = New-Object $foo
$foo2.Age = 45
$foo2.Name = 'John'
$foo2

$foo2 -gt $foo1
Keith Hill
As I mentioned in the question I was looking for a way to do this without using C# and Add-Type. My reason is that I wan't my entire script to be easily debuggable from within Powershell and I want to be able to make changes without having to restart my Powershell session
Willbill
Sorry, must of missed the part about not using Add-Type. One way to "work-around" that problem is to parameterize the type name and change it between executions when the C# has changed. Otherwise you can test if the type already exists and skip the Add-Type call.
Keith Hill
I really don't like the inflexibility in the way that c# is integrated but a good answer none the less
Willbill
FWIW I'm also annoyed when I have to keep changing the type name as I debug the C# part of the script but when the typename is parameterized at least the pain is minimized. :-)
Keith Hill