How to Pass Properties as "Out" or "Ref" Parameters

Passing properties by reference in C#

Properties cannot be passed by reference. Here are a few ways you can work around this limitation.

1. Return Value

string GetString(string input, string output)
{
if (!string.IsNullOrEmpty(input))
{
return input;
}
return output;
}

void Main()
{
var person = new Person();
person.Name = GetString("test", person.Name);
Debug.Assert(person.Name == "test");
}

2. Delegate

void GetString(string input, Action<string> setOutput)
{
if (!string.IsNullOrEmpty(input))
{
setOutput(input);
}
}

void Main()
{
var person = new Person();
GetString("test", value => person.Name = value);
Debug.Assert(person.Name == "test");
}

3. LINQ Expression

void GetString<T>(string input, T target, Expression<Func<T, string>> outExpr)
{
if (!string.IsNullOrEmpty(input))
{
var expr = (MemberExpression) outExpr.Body;
var prop = (PropertyInfo) expr.Member;
prop.SetValue(target, input, null);
}
}

void Main()
{
var person = new Person();
GetString("test", person, x => x.Name);
Debug.Assert(person.Name == "test");
}

4. Reflection

void GetString(string input, object target, string propertyName)
{
if (!string.IsNullOrEmpty(input))
{
var prop = target.GetType().GetProperty(propertyName);
prop.SetValue(target, input);
}
}

void Main()
{
var person = new Person();
GetString("test", person, nameof(Person.Name));
Debug.Assert(person.Name == "test");
}

Is it possible to pass properties as out or ref parameters?

Apologies for the short answer, but no, the C# language specification disallows it.

See this answer to another question to see what happens when you try.
It also says why you shouldn't make the property just be a public field to get around the restriction.

Hope this helps

EDIT: You ask Why?

You pass a variable to an out or ref parameter you're actually passing the address (or location in memory) of the variable.
Inside the function the compiler knows where the variable really is, and gets and writes values to that address.

A property looks like a value, buts it's actually a pair of functions, each with a different signature. So to pass a property, you'd actually need to pass two function pointers, one for the get, and one for the set.

Thats a completely different thing to pass to a function than the address of a variable

i.e. one variable address v's two function pointers.

Update
Why doesn't C# just look after this for us?

I'm no Eric Lippert, but I'll have a go at why

What should the signature of the function you're calling be?

Lets say you want to call void MyFn(ref int i) should that remain that way, or should it change to say we also allow properties? If it changes to some syntax like void MyFn(prop_ref int i) then this is fairly useless, you can't pass properties to library functions or 3rd party code that wasn't written with the special prop_ref modifier. Anyway I think you're suggesting it shouldn't be different.

Now lets say MyFn passes i to a COM function, or WinAPI call, passing the address of i (i.e. outside .net, by ref). If it's a property, how do you get the address of i? There may be no actual int under the property to get the address of. Do you do what VB.Net does?

The Vb.Net compiler spots when a property is passed as a ByRef argument to a method. At that point it declares a variable, copies the property to the variable, passes the variable byref and then after the method is called, copies the variable back into the property. i.e.

MyFunc(myObject.IntProperty)

becomes

Dim temp_i As Integer = myObject.IntProperty
MyFunc(temp_i)
myObject.IntProperty = temp_i

Any property side effects don't happen until MyFunc returns, which can cause all sorts of problems and lead to very subtle bugs.

In my humble opinion the Vb.Net solution to this problem is also broken, so I'm not going to accept that as an answer.

How do you think the C# compiler should handle this?

Is it possible to pass a property as a ref parameter in a setter? c# (avoid a property or indexer may not be passed as an out or ref parameter.)

No, basically.

There is a potential loophole around mutable ref-returning get-only properties, but that probably won't help you because:

  1. you can't have any set logic in a mutable ref-returning get-only property
  2. this is an absurdly niche scenario that the majority of devs don't know exists, and will never need to use :)

A property or indexer may not be passed as an out or ref parameter

you cannot use

double.TryParse(objReader[i].ToString(), out bd.Budget); 

replace bd.Budget with some variable.

double k;
double.TryParse(objReader[i].ToString(), out k);

how to pass a property by reference in C#

You could write an ugly extension method that takes an expression representative of the property you want to set, and give it a chance to check whether your new values are null or empty (or different from the destination) before assigning the value.

public static void SetPropertyValue<T>(this T target, Expression<Func<T, string>> memberLamda, string value)
{
// Check if "new value" is null or empty and bail if so
if (string.IsNullOrEmpty(value))
return;

var memberSelectorExpression = memberLamda.Body as MemberExpression;
if (memberSelectorExpression != null)
{
var property = memberSelectorExpression.Member as PropertyInfo;
if (property != null)
{
// Get the existing value and compare against the new value
// Only set the property if it's different from the existing value
if ((string)property.GetValue(target, null) != value)
{
property.SetValue(target, value, null);
}
}
}
}

Source

And then you could use it like:

anObject.SetPropertyValue(a => a.SampleText1, value1);
anObject.SetPropertyValue(a => a.SampleText2, value2);

This should allow you to avoid having the object marked as "dirty", but is rather expensive (as Marc mentioned in a comment on his answer).

Why a property can not be passed as out parameter?

This is valid in VB, but not in C#... VB effectively creates a temporary local variable for you, calls the method passing in the local variable as the argument, and then sets the property with the value of the local variable. C# doesn't usually hide that sort of thing for you.

The method itself needs a variable as the out parameter. It's got to have a storage location it can just write values to. Not a property, not anything it needs to invoke: just a storage location. A property doesn't satisfy that requirement. So there's nothing that can be done by the compiler in the method to allow this.

So either the compiler has to fake it with a temporary variable, as per VB, or disallow it, as per C#. Personally I prefer the C# approach - otherwise it looks as if each time the method assigned a value to the out parameter, the property would be set - which certainly isn't the case.

Passing a property as an 'out' parameter in C#

You'd have to explicitly use a field and "normal" property instead of an auto-implemented property:

public class Bob
{
private int value;
public int Value
{
get { return value; }
set { this.value = value; }
}
}

Then you can pass the field as an out parameter:

Int32.TryParse("123", out bob.value);

But of course, that will only work within the same class, as the field is private (and should be!).

Properties just don't let you do this. Even in VB where you can pass a property by reference or use it as an out parameter, there's basically an extra temporary variable.

If you didn't care about the return value of TryParse, you could always write your own helper method:

static int ParseOrDefault(string text)
{
int tmp;
int.TryParse(text, out tmp);
return tmp;
}

Then use:

bob.Value = Int32Helper.ParseOrDefault("123");

That way you can use a single temporary variable even if you need to do this in multiple places.

C# Properties as Reference

No, properties that are passed into methods are not modified in that method, because you cannot pass them by reference. Properties are always passed by value, so you cannot change their value within the methods that you pass them two.

However, that's not actually what you are doing in both methods, and that's why one works and one does not.

Your first method:

public void ChangeCountry(string _Country)
{
_Country = "US";
}

attempts to change the value of the parameter that does not have the ref keyword, which fails (as you noted).

Your second method,

public void ChangeAddress(Address _adr)
{
_adr.Country = "US";
}

is not trying to change the value of its parameter. The Address object that you pass into the method remains the same instance of Address for the entire method. However, because Address is a reference type, you can change its properties, and those changes will persist once the method returns. They will also be visible to any other places that have a reference to the same instance.

To see the difference, if you had tried to do this in your second method, it would have failed, for the same reason your first method does:

public void ChangeAddress(Address _adr)
{
_adr = new Address();
_adr.Country = "US";
}

Here, you are trying to change the value of the parameter -- to a new instance of the class -- and that doesn't work without the ref keyword.

Why can fields be used as out/ref parameters and not properties?

The biggest difference is that properties or indexers may not be passed as ref or out parameter (demo).

This is because properties do not necessarily have a backing storage - for example, a property may be computed on the fly.

Since passing out and ref parameters requires taking variable's location in memory, and because properties lack such location, the language prohibits passing properties as ref/out parameters.

C# property and ref parameter, why no sugar?

Just for info, C# 4.0 will have something like this sugar, but only when calling interop methods - partly due to the sheer propensity of ref in this scenario. I haven't tested it much (in the CTP); we'll have to see how it pans out...



Related Topics



Leave a reply



Submit