Passing Properties by Reference in C#

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");
}

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).

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.

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 :)

C# Pass a property by reference

You can call the function with a lambda expression:

private void setFromQueryString<T>(Action<T> setter, String queryString, HttpContext context) 
{
//here I want to handle pulling the values out of
//the query string and parsing them or setting them
//to null or empty string...
String valueString = context.Request.QueryString[queryString].ToString();

//I need to check the type of the property that I am setting.

//this is null so I can't check it's type
Type t = typeof(T);
...
setter(value);
}

You would call it like this:

setFromQueryString<int>(i => myFoo.Age = i, "inputAge", context);

EDIT: If you really want type inference:

private void setFromQueryString<T>(Func<T> getter, Action<T> setter, String queryString, HttpContext context) {
...
}
setFromQueryString(() => myFoo.Age, i => myFoo.Age = i, "inputAge", context);

c# pass property backing value by reference, is it possible?

There is no valid identifier for the backing field for that property. You could not use an auto property, and instead explicitly define the get and set methods of the property, along with your own backing field, thus giving you a valid identifier for the backing field, although it would be very poor design to expose this backing field externally.

What you should do is re-design your code such that you don't need to pass the value by reference in the first place. You should just be passing the string by value and, if the result of this function is the computation of a string, returning it. The caller can then set that string back to the property if that's what they want. That would be the more idiomatic design. (Since you also have a boolean value you'd need to pass both the string and the boolean out, of course.)

Are properties passed by reference in C#?

You need to use the ref keyword to pass by reference in C#. In C#, pass by value is the default. This is a little confusing when you pass a non-immutable class instance, because what you're passing is the value of a reference. When you pass a class reference to a method, without the ref keyword, the method can change the properties of the instance, but they can't change the caller's reference to that instance. They can't change the caller's reference to refer to a new instance. But with ref (or out), they can.

The ref keyword in C# can't be used with a property. See fiddle for the code below:

Compilation error (line 8, col 15): A property, indexer or dynamic member access may not be passed as an out or ref parameter

C#

using System;

public class Program
{
public static void Main()
{
var c = new C();
F(ref c.P);
}

public static void F(ref int n) { }

public class C
{
public int P { get; set; }
}
}

This is because ref int is a reference to an int, and a property with a getter and a setter is not an integer. It's code that returns an integer, or can have an integer assigned to it. C# does some magic, but not that much magic. And "magic" is just code generated by the compiler. They could have done that. But they didn't.

I would guess that they wanted to keep it as simple syntactic sugar on a pointer (or whatever thing closely analogous to it) for the sake of runtime efficiency. But that guess could be hilariously wrong.

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?



Related Topics



Leave a reply



Submit