How do I modify the contents of a boxed Windows Runtime value?

Raymond Chen

When you box a value in the Windows Runtime, it’s boxed for good. You can’t change the value inside a box. Most of the time, it’s pretty obvious that you can’t do it:

// C#
void UpdateX(Nullable<Point> pt)
{
  if (pt != null) {
    // Error: Cannot modify the return value of Point?.Value
    // because it is not a variable
    pt.Value.X = 42.0;
  }
}

// C++/WinRT
void UpdateX(IReference<Point> pt)
{
  if (pt != nullptr) {
    // Error: expression must be a modifiable lvalue
    pt.Value().X = 42.0;
  }
}

// C++/CX
void UpdateX(IBox<Point>^ pt)
{
  if (pt != nullptr) {
    // Error: lvalue required as left operand of assignment
    pt->Value.X = 42.0;
  }
}

In the case of C++/WinRT, I think the error is unlikely to occur because you’re clearly modifying the result of a function call.

But for C# and C++/CX, the property syntax looks a lot like a member variable access, and you may not realize that the property value is secretly the result of a function call.

Boxed values are read-only. If you look at IReference<T> (which is the interface at the ABI layer that is used for Windows Runtime boxed values), you’ll see that there is a read-only Value property, but no method for setting a new value. Once a value is boxed, you can’t change it.

But what if you want to change it?

You’ll have to box up a new value and then ask everybody to switch over to it.

// C#
Nullable<Point> UpdateX(Nullable<Point> pt)
{
  if (pt != null) {
    var value = pt.Value;
    value.X = 42.0;
    pt = value; // box up a new value
  }
  return pt;
}

// C++/WinRT
IReference<Point> UpdateX(IReference<Point> pt)
{
  if (pt != nullptr) {
    auto value = pt.Value();
    value.X = 42.0;
    pt = winrt::box_value(value); // box up a new value
  }
  return pt;
}

// C++/CX
IBox<Point> UpdateX(IBox<Point>^ pt)
{
  if (pt != nullptr) {
    auto value = pt->Value;
    value.X = 42.0;
    pt = value; // box up a new value
  }
  return pt;
}

Of course, you now have to take the updated boxed value and update wherever you got it from.

// C#
flyoutShowOptions.Position = UpdateX(flyoutShowOptions.Position);

// C++/WinRT
flyoutShowOptions.Position(UpdateX(flyoutShowOptions.Position()));

// C++/CX
flyoutShowOptions->Position = UpdateX(flyoutShowOptions->Position);

9 comments

Discussion is closed. Login to edit/delete existing comments.


Newest
Newest
Popular
Oldest
  • Michael Liu 1

    “Boxing” means the conversion of a value type to a reference type. In your C# example, there is no boxing or unboxing; the compiler error occurs not because pt.Value is “boxed” but rather because a struct returned by a property is classified as a read-only value instead of a variable. Here’s a better example:

    void UpdateX(object? pt)
    {
        if (pt != null) {
            // Error: Cannot modify the result of an unboxing conversion
            ((Point)pt).X = 42.0;
        }
    }
    
    • Mystery Man 0

      You are correct, and there is more. Passing the boxed value as a function parameter defeats the purpose of showing anything about writing to a boxed value. Even if pt was an integer, writing to it would have served no purpose.

      This whole blog post looks like a justification looking for a problem. (So far, I had only seen people writing solutions in search of a problem!)

      • Raymond ChenMicrosoft employee Author 1

        This blog post is an answer to a real question from a customer. So there was at least one person who had this problem of wanting to modify a boxed value.

      • Mystery Man · Edited 0

        Did the customer also receive the boxed value through a method’s parameter? Or did their situation have merit?

        I’m not asking questions; they are rhetorical. What I mean to say is, cutting down the noise brings out the point you want to make. That’s all.

      • Raymond ChenMicrosoft employee Author 0

        The customer wanted to create a boxed Point with a specific value, and their plan was to create a boxed point and then modifying the boxed value: IBox^ pt = Point(); pt->Value.X = 42.0f; pt->Value.Y = 32.0f;

      • Me Gusta 0

        Well, if you are writing a Windows Runtime component then yes it is very possible that this could come in through the parameters. Since the component would also have to project back to the Windows Runtime type system, then this situation in general is valid.
        You can also see this entire situation in the UWP, one example that I know of is Windows.UI.WindowManagement.AppWindowTitleBar.BackgroundColor. For C++/WinRT this has the signature of:

        IReference<Color> BackgroundColor();
        
        void BackgroundColor(IReference<Color> value);

        For C# this has the signature of:

        public System.Nullable<Color> BackgroundColor { get; set; }

        If you were implementing something like this, what would the setter parameter be?

      • 紅樓鍮 0

        The box is a reference type, and in C# reference type objects are usually used as containers of mutable values whose mutation is observed by every party holding a reference to the object. Raymond points out that contrary to that pattern, the contents of the box is immutable, and therefore it cannot be used for shared mutation.

      • Mystery Man 0

        And I am pointing out that Mr. Chen’s demonstration has too much noise that obscures the main point. The function definition, the parameter, the Nullable class, and even the mention of Windows Runtime are extra.

        C# features cooperative autoboxing of primitive types via the Object class. But if you type out the code snippet of Michael Liu, the .NET compiler will say, “Cannot modify the result of an unboxing conversion.” Thankfully, that message makes it clear that the reason has to do with unboxing, not an attempt to modify a method parameter.

    • 紅樓鍮 · Edited 0

      Raymond is probably talking about “boxing” in the Windows Runtime sense, not the .NET sense.

Feedback usabilla icon