October 24th, 2025
like2 reactions

The early history of the Windows Runtime PropertyValue and why there is a PropertyType.Inspectable that is never used

Some time ago, I mentioned that the Property­Value.Create­Inspectable method doesn’t create a Property­Value but instead just returns its parameter unchanged. I concluded with the remark, “Property­Value.Create­Inspectable even exist? I’m not sure. Perhaps it was added for completeness.”

Since that article was written, I did some archaeology, and I think I found the answer.

Originally, the Property­Value did wrap inspectables, which is the Windows Runtime name for what most languages call an “object”. The idea was that the Property­Set would be an IMap<String, Property­Value>, meaning that it was an associative array mapping strings to Property­Value objects. These Property­Value objects could hold value types like integers, or they could hold reference types in the form of an IInspectable, or they could hold arrays of value or reference types, or they could hold nothing at all (“empty”).

In that original design, the Property­Value.Create­Inspectable method did return a Property­Value whose Type was Property­Type.Inspectable. Similarly, the Property­Value.Create­Empty method returned a Property­Value whose Type was Property­Type.Empty.

Value PropertyType
pv.Type
Creation Retrieval
Nothing Empty CreateEmpty() N/A
Scalar type (e.g. Int32) Int32 CreateInt32(v) pv.GetInt32()
Reference type Inspectable CreateInspectable(v) pv.GetInspectable()
Array type (e.g. Int32[]) Int32Array CreateInt32Array(v) pv.GetInt32Array()

You had to wrap inspectables and empties because the Property­Set was a mapping from strings to Property­Value. Everything had to be expressed as a Property­Value.¹ The Property­Value was the fundamental variant type of the Windows Runtime.

At some point, the team decided to change the design and let Property­Set to be a mapping from string to inspectable (object). If the associated value is an object, then you get the corresponding object. If the associated value is empty, then you get null. And if the associated value is a value type, then the value type is wrapped inside a Property­Value, and the Property­Value wrapper acts as the object.

I don’t know why the team changed their mind, but I suspect one of the points in favor of the new design is that the new design matches how most languages already work: C#, JavaScript, Python, Ruby, Scheme, Java, Objective-C all follow the principle that any type can be expressed as an object. (The act of converting value types to objects is known as “boxing”.)

In other words, the design change promoted IInspectable to be the fundamental variant type, and the Property­Value was demoted to being the fundamental boxed type.

A very large change was made to the Windows code base to update the design and to update all the code that had been using the old design. To make the conversion easier, the general shape of the new design matched the old design where it made sense. And this meant that Property­Value.Create­Inspectable and Property­Value.Create­Empty still existed as functions, but they didn’t do anything interesting. They remained for backward compatibility. Also remaining for backward compatibility are the enumeration values Empty and Inspectable. They remain defined, but nobody uses them.

That takes us to what we have today:

Value PropertyType
pv.Type
Creation Retrieval
Nothing N/A null null
Scalar type (e.g. Int32) Int32 CreateInt32(v) pv.GetInt32()
Reference type N/A v pv
Array type (e.g. Int32[]) Int32Array CreateInt32Array(v) pv.GetInt32Array()

For backward compatibility, Create­Empty returns null and Create­Inspectable returns its (non-null) parameter unchanged.

In addition to aligning closer to a large number of popular languages, the new design simplifies the code required to store and retrieve a possibly-null reference in a Property­Set.

// C#

// Old design: CreateInspectable requires a non-null reference
ps.Insert("key", v == null ? PropertyValue.CreateEmpty() : PropertyValue.CreateInspectable(v));

pv = ps.Lookup("key");
if (pv.Type == PropertyValue.Empty) {
    v = null;
} else {
    v = pv.GetInspectable();
}

// New design: If it's null, then store null
ps.Insert("key", v);

v = ps.Lookup("key");

If you want a function that returns the Property­Type for an arbitrary inspectable, you can do this:

// C#
PropertyType WhatIsThisThing(object thing)
{
    if (thing is IPropertyValue pv) {
        return pv.Type;
    } else if (thing is null) {
        return PropertyType.Empty;
    } else {
        return PropertyType.Inspectable;
    }
}

// C# 9.0
PropertyType WhatIsThisThing(object thing)
{
    return thing switch {
        null => PropertyType.Empty,
        IPropertyValue pv => pv.Type,
        _ => PropertyType.Inspectable,
    };
}

// C++/WinRT
PropertyType WhatIsThisThing(IInspectable const& thing)
{
    if (thing == nullptr) {
        return PropertyType::Empty;
    } else if (auto pv = thing.try_as<IPropertyValue>()) {
        return pv.Type();
    } else {
        return PropertyType::Inspectable;
    }
}

¹ Since Property­Value is a reference type, they could have decided to use a null pointer to represent the empty state. I assume they explicitly wrapped the empty state for uniformity, rather than forcing people to check for null before querying the Type. Compare the JsonValue which has an explicit object for representing the JSON null rather than using a null pointer.

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments