3

I have created a void extension method which can be used with decimal data types. I wanted to be able to modify the this parameter variable inside the scope of the method. This is the code for my extension method:

public static void SetAndConvertIfHasValue(this decimal assignTo, double? valueToAssign)
{
    if (valueToAssign.HasValue)
        assignTo = (decimal)valueToAssign.Value;
    else
        assignTo = 0m;
}

However, when I call it:

data.MyDecimalToSet.SetAndConvertIfHasValue(nullableDouble);

data.MyDecimalToSet is not set to the value in nullableDouble if it has one.

In debug if I step into the extension method, assignTo is changed to the correct value, this change just doesn't bubble up to data.MyDecimalToSet.

At this point I have decided to use a standard method rather than an extension method as a solution to this problem, however I was curious as to why this doesn't work? And whether there is a way around it, or if it simply is impossible?

GaryEmery
  • 278
  • 2
  • 13
  • 1
    If you want to get crazy, you can create an extension method that takes an `Expression` in addition to the `double?`, unwraps it and sets the property using a bit of reflection. Then you would call it on the base object rather than the property, like so: `data.SetAndConvertIfHasValue(d => d.MyDecimalToSet, nullableDouble)`. It's uglier and slower, but it works! – siride Jan 11 '16 at 14:22

1 Answers1

4

This doesn't work because when you pass a variable to a method, a copy of that variable is passed. When you operate on that copy, you will only be changing the copy.

Note that this will happen regardless of whether the variable is a reference or a value type. However, if you pass a reference type, you can change the contents of the reference type, since the reference passed to the method will be copied, but it will still reference the original object.

(decimal is a value type, so that last point does not apply to it.)

You are doing it the right way if you use a standard method instead.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 1
    I thought this was the reason. I tried passing it as `ref` but that is not supported with extension methods. – GaryEmery Jan 11 '16 at 14:09
  • 2
    Don't use "stack" and "heap" in these sorts of explanations as they are implementation details. Copy and reference semantics would be better. In any case, even if a reference type were used here, the same problem would occur because you can't modify variables/properties/fields that are passed in unless they are passed with `ref` or `out`. It has nothing, therefore, to do with value types vs. reference types. – siride Jan 11 '16 at 14:14
  • @siride I presume, like `ref`, you can't use `out` in extension methods? – GaryEmery Jan 11 '16 at 14:16
  • @GaryEmery: correct (because in the CLR, `ref` and `out` are the exact same thing -- only in C# does the compiler enforce a difference with the `OutAttribute`). More importantly, though, is that you can't pass properties to extension methods or any methods as `ref`/`out`. – siride Jan 11 '16 at 14:19
  • @siride I've corrected my answer somewhat. – Matthew Watson Jan 11 '16 at 16:16