Retrieving Properties from PSObject ExtensionData Using Reflection

Oh, PowerShell. Why do you do this to me? I run a query from your command line and see a bounty of properties, But, alas, when run from code, the properties that I expect are missing! Where could they be? ExtensionData? Ugh.

Now, it really seems like getting property values out of ExtensionData shouldn’t be so hard. It was difficult and annoying enough to me that I feel like I must be doing something wrong. If you know of a better way, please–PLEASE–let me know. Until then, I present to you a method for accessing the properties using reflection.

In this example, I’m essentially trying to access the following path of a PSObject:

psObject.ExtensionData.Value.Members
    .FirstOrDefault(x => x.Name == "propertyName").Value.Value

Things start off pretty straightforward. We want to get the ExtensionData property, which is included in the PSObject‘s Members collection.

var extensionData = psObject.Members["ExtensionData"].Value;

extensionData has its own Members collection, so we get that next. It’s not a public property, though, so we have to dig it out using reflection. Also note that we cast the object to an IEnumerable<object>

var members = extensionData.GetType()
    .GetProperty("Members", BindingFlags.NonPublic | BindingFlags.Instance)
    .GetValue(extensionData) as IEnumerable<object>;

Things are starting to get a little trickier. We need to reflect on the members to find the property name that we’re looking for.

var memberType = members.First().GetType();
var nameProperty = memberType.Getproperty("Name", BindingFlags.Public | BindingFlags.Instance);
var member = members
    .Where(x => string.equals(propertyName, nameProperty.GetValue(x) as string, 
        StringComparison.InvariantCultureIgnoreCase))
    .FirstOrDefault();

Now we’re in the home stretch, and we just need to get the property value. One caveat, though: the property is a data node, so you actually need to get its value. That’s right, we need Value.Value.

var valueProperty = memberType.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
var value = valueProperty.GetValue(member);
valueProperty = value.GetType().GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
return valueProperty.GetValue(value) as string;

It got kinda gross in the end there, but mission accomplished. I found that the data types weren’t preserved in the extension data, so I had to return values as strings and cast to the appropriate data type (e.g., bool) outside my function.

Here’s the complete solution. (Error/null-checks omitted for brevity.)

string GetPropertyFromExtensionData(PSObject psObject, string propertyName)
{
    var extensionData = psObject.Members["ExtensionData"].Value;

    // members = extensionData.Members as IEnumerable<object>
    var members = extensionData.GetType()
        .GetProperty("Members", BindingFlags.NonPublic | BindingFlags.Instance)
        .GetValue(extensionData) as IEnumerable<object>;

    // member = members.Where(x => x.Name == propertyName)
    var memberType = members.First().GetType();
    var nameProperty = memberType.Getproperty("Name", BindingFlags.Public | BindingFlags.Instance);
    var member = members
        .Where(x => string.equals(propertyName, nameProperty.GetValue(x) as string, 
            StringComparison.InvariantCultureIgnoreCase))
        .FirstOrDefault();

    // return member.Value.Value as string
    var valueProperty = memberType.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
    var value = valueProperty.GetValue(member);
    valueProperty = value.GetType().GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
    return valueProperty.GetValue(value) as string;
}
Advertisement

Call Method Overloads Based on Derived Type

I was creating a data access component that performed different operations based on the type of request that it received. In order to accommodate this, I have an abstract base request type defined, and handled requests derive from this type. Without getting too deep into why I had to do it this way, I had overloaded methods to which I needed to “route” an instance of the abstract class.

.NET 4 gives us an easy way to do this with the dynamic keyword. Consider the following example, which will correctly call the correct method overload:

void Main()
{
    MyBase obj = new MyOne();
    var p = new Printer();
    p.Print(obj);
}

public class Printer
{
    public void Print(MyBase item)
    {
        dynamic i = item;
        Print(i);
    }
    
    public void Print(MyOne item)
    {
        Console.WriteLine("Print(MyOne)");
    }
}

public abstract class MyBase
{
}

public class MyOne : MyBase
{
}

dynamic wasn’t introduced until .NET 4, though. So, if you have an older application, it may not be available to you. The good news is that you can accomplish the same goal by using reflection. It’s just as effective, but it’s a bit gnarlier.

public void Print(MyBase item)
{
    this.GetType()
        .GetMethod("Print", new[] { item.GetType() })
        .Invoke(this, new object[] { item });
}

So there you have it–two different ways to call method overloads based on object type!

Abstract Classes and Generic Methods

I ran into a fun scenario where I passing an object that derived from an abstract base class into a generic static method where it would be passed on to another generic method for some special, type-specific processing. Everything worked great when I passed in an object declared as one of the derived types. However, when the object was declared as the abstract base type, it all fell apart because the generic method wanted to treat the object as the base type and not the actual, derived type!

Problem

Consider this re-invented version of the scenario. I have a static PatientRouter class that accepts injured Creatures. If the Creature is a Human, it will be routed to a HumanHospital. If it’s an Animal, it will be routed to an AnimalHospital. Note, however, that the code fails if a Creature object is received, even if the Creature is actually a Human or Animal. We need to do something so this Creature can be correctly cared for!

public static class PatientRouter
{
    public static void Route<T>(T creature) 
        where T : Creature
    {
        SendToHospital(creature);
    }

    public static void SendToHospital<T>(T creature)
        where T : Creature
    {
        if (typeof(T) == typeof(Creature))
        {
            throw new Exception("Unacceptable!");
        }
        if (typeof(T) == typeof(Human))
        {
            var h = new HumanHospital();
            h.CareFor(creature as Human);
        }
        if (typeof(T) == typeof(Animal))
        {
            var h = new AnimalHospital();
            h.CareFor(creature as Animal);
        }
    }
}

Solution

There are two options that I found for dealing with this scenario. The first is to use the dynamic keyword introduced in .NET Framework 4.0. Here’s what that might look like:

public static void Route<T>(T creature)
    where T : Creature
{
    dynamic d = creature;
    SendToHospital(d);
}

Unfortunately, .NET 4.0 wasn’t an option for me, though. I was, however, able to come up with an acceptable solution using reflection. I don’t love using reflection to execute a method like this, but it gets the job done–so I’m content to use it in a scenario like this until .NET 4.0 becomes available.

public static void Route<T>(T creature) 
    where T : Creature
{
    // using reflection
    typeof(PatientRouter)
        .GetMethod("SendToHospital")
        .MakeGenericMethod(creature.GetType())
        .Invoke(null, new[] { creature });
}

For reference, here’s the complete example with both solutions:

namespace samples.BaseClassGenericMethod
{
    using System;

    public abstract class Creature
    {
        public bool IsInjured { get; set; }
    }

    public class Human : Creature
    {
        /* human stuff */
    }

    public class Animal : Creature
    {
        /* animal stuff */
    }

    public interface IHospital<T> where T : Creature
    {
        void CareFor(T patient);
    }

    public class HumanHospital : IHospital<Human>
    {
        public void CareFor(Human patient)
        {
            Console.WriteLine("Caring for human!");
        }
    }

    public class AnimalHospital : IHospital<Animal>
    {
        public void CareFor(Animal patient)
        {
            Console.WriteLine("Caring for animal!");
        }
    }

    public static class PatientRouter
    {
        public static void Route<T>(T creature) 
            where T : Creature
        {
            // base case
            SendToHospital(creature);

            // using dynamic (.NET 4.0)
            //dynamic d = creature;
            //SendToHospital(d);

            // using reflection
            //var h = typeof(PatientRouter)
            //    .GetMethod("SendToHospital")
            //    .MakeGenericMethod(creature.GetType())
            //    .Invoke(null, new[] { creature });
        }

        public static void SendToHospital<T>(T creature)
            where T : Creature
        {
            if (typeof(T) == typeof(Creature))
            {
                throw new Exception("Unacceptable!");
            }
            if (typeof(T) == typeof(Human))
            {
                var h = new HumanHospital();
                h.CareFor(creature as Human);
            }
            if (typeof(T) == typeof(Animal))
            {
                var h = new AnimalHospital();
                h.CareFor(creature as Animal);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var h = new Human();
            PatientRouter.Route(h);

            var a = new Animal();
            PatientRouter.Route(a);

            Creature c = new Human();
            PatientRouter.Route(c);

            Console.ReadLine();
        }
    }
}
%d bloggers like this: