295

I'm trying to do do the following:

GetString(
    inputString,
    ref Client.WorkPhone)

private void GetString(string inValue, ref string outValue)
{
    if (!string.IsNullOrEmpty(inValue))
    {
        outValue = inValue;
    }
}

This is giving me a compile error. I think its pretty clear what I'm trying to achieve. Basically I want GetString to copy the contents of an input string to the WorkPhone property of Client.

Is it possible to pass a property by reference?

AFract
  • 8,868
  • 6
  • 48
  • 70
yogibear
  • 14,487
  • 9
  • 32
  • 31
  • 1
    As to why, see this http://stackoverflow.com/questions/564557/is-it-possible-to-pass-properties-as-out-or-ref-parameters – nawfal Feb 11 '13 at 12:04
  • 1
    I recommend people also look at this post for ideas involving extension methods: https://stackoverflow.com/a/9601914/4503491 – Red Riding Hood Jul 20 '21 at 16:42

16 Answers16

521

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");
}
Nathan Baulch
  • 20,233
  • 5
  • 52
  • 56
  • 3
    Love the examples. I find that this is a great place for extension methods too: `code`public static string GetValueOrDefault(this string s, string isNullString) { if (s == null) { s = isNullString; } return s; } void Main(){ person.MobilePhone.GetValueOrDefault(person.WorkPhone); } – BlackjacketMack Sep 04 '12 at 17:00
  • 11
    In solution 2, the 2nd parameter `getOutput` is unnecessary. – Jaider Nov 05 '12 at 17:00
  • 38
    And I think a better name for the solution 3 is Reflection. – Jaider Nov 05 '12 at 17:07
  • 2
    In solution 2, the 2nd parameter getOutput is unnecessary - true but I used it inside GetString to see what the value was that I was setting. Not sure how to do that without this parameter. – Petras Mar 24 '13 at 11:45
  • 5
    Solution 3 using *reflection* AND *Linq expressions* is very elegant and did the job nicely. 4 years later and still well done :) – iCollect.it Ltd Jun 06 '13 at 13:40
  • 9
    @GoneCodingGoodbye: but the least efficient approach. Using reflection to simply assign a value to a property is like taking a sledgehammer to crack a nut. Also, a method `GetString` that is supposed to set a property is clearly misnamed. – Tim Schmelter Feb 11 '16 at 12:32
  • 2
    @TimSchmelter: Efficiency is relative. It is always a balance between speed & maintainability. Don't suffer from premature optimization! :) – iCollect.it Ltd Feb 11 '16 at 12:35
  • @GoneCodingGoodbye: What is more maintainable there? It's confusing code for no reason. This is also against the nature of LINQ which is supposed to query something and not modifying it. A LINQ query should not cause side effects. If you would use this method in a query you would clearly cause side effects which can lead to incorrect results. – Tim Schmelter Feb 11 '16 at 12:37
  • @TimSchmelter: In that specific example it is simple calling code. The interior implementation of many functions can be torture, but it is self-contained. Confusing is in the eye of the beholder (and where you look) :) – iCollect.it Ltd Feb 11 '16 at 12:40
  • 1
    3 and 4 solutions are also good for cases when method decides what value should be set to property – Rafael Seidalinov Sep 21 '16 at 15:06
  • Personally I love #4. I have a common function that needed to work with Properties of several of my Models. The function need to actually make changes to the properties that don't carry up through the call stack, and without the "ref" available.. this totally helped me out. – da_jokker Jan 30 '19 at 22:28
  • @da_jokker Yes, same here. I'm using it in WPF + MVVM pattern to pass property to a method, then I'm able to use this property as a parameter and assign e.g. string to it. – GoodOldGuy Feb 28 '20 at 15:22
  • @GoodOldGuy, I used the similar to #4 approach for a long time in my WPF+MVVM project to implement a `RaiseAndSetIfChanged` behavior. It worked great until I've applied an obfuscator and the code stopped working (because of obfuscating property names). That is why I'm here rewriting my code to use #2. #1 (the Return Value way) doesn't suit because you should set a new value to the property before raising OnPropertyChange() in the called method. Thanks, @Nathan Baulch for your answer (and for providing different! – Mar Feb 13 '22 at 14:59
  • Regarding solution #3, here's a solution that uses an expression tree without relying on reflection.. stackoverflow.com/a/73762917/227110 – stoj Sep 18 '22 at 13:00
40

I wrote a wrapper using the ExpressionTree variant and c#7 (if somebody is interested):

public class Accessor<T>
{
    private Action<T> Setter;
    private Func<T> Getter;

    public Accessor(Expression<Func<T>> expr)
    {
        var memberExpression = (MemberExpression)expr.Body;
        var instanceExpression = memberExpression.Expression;
        var parameter = Expression.Parameter(typeof(T));

        if (memberExpression.Member is PropertyInfo propertyInfo)
        {
            Setter = Expression.Lambda<Action<T>>(Expression.Call(instanceExpression, propertyInfo.GetSetMethod(), parameter), parameter).Compile();
            Getter = Expression.Lambda<Func<T>>(Expression.Call(instanceExpression, propertyInfo.GetGetMethod())).Compile();
        }
        else if (memberExpression.Member is FieldInfo fieldInfo)
        {
            Setter = Expression.Lambda<Action<T>>(Expression.Assign(memberExpression, parameter), parameter).Compile();
            Getter = Expression.Lambda<Func<T>>(Expression.Field(instanceExpression,fieldInfo)).Compile();
        }

    }

    public void Set(T value) => Setter(value);

    public T Get() => Getter();
}

And use it like:

var accessor = new Accessor<string>(() => myClient.WorkPhone);
accessor.Set("12345");
Assert.Equal(accessor.Get(), "12345");
Sven
  • 11,442
  • 2
  • 16
  • 20
  • 3
    Best answer here. Do you know what is the performance impact? It would be nice to have it covered within answer. I'm not familiar with the expression trees much but I would expect, that using Compile() means the accessor instance contains actually IL compiled code and therefore using constant number of accessors n-times would be okay, but using total of n accessors (high ctor cost) would not. – mancze Sep 15 '17 at 08:16
  • Great code! My opinion, it is the best answer. The most generic one. Like says mancze... It should have a huge impact on performance and should be used only in a context where code clarity is more important than perfomance. – Eric Ouellet May 06 '20 at 11:35
  • 2
    @EricOuellet _"It should have a huge impact on performance"_. Based on what? Assuming the `Accessor` class is not recreated each time, I would expect the calls to Get() and Set() to have minimal impact on performance. Of course, the correct answer is to measure it and find out. – bornfromanegg Oct 26 '20 at 11:46
  • Great code!!!! I love it. Just want to say about performance that I was wrong and just realised that it was compiled. I'm re-using it now and should re-use it more. – Eric Ouellet Jun 02 '21 at 20:35
  • That's inspired thinking by creating custom expressions for the setter/getter based on the compiler emittted expression from the lambda expression passed in via the ctor. FYI, I've taken it step further by removing the unnecessary reflection, custom getter expression, and speicalized field expressions. https://stackoverflow.com/a/73762917/227110 – stoj Sep 18 '22 at 12:58
  • Seeing the accessor syntax, it very much reminds me of ReactiveUI's WhenAny(), and if the goal of passing around a reference is to be able to observe changes in values, then using IObservable might be the better choice. – Alexander Gräf Apr 25 '23 at 21:38
34

without duplicating the property

void Main()
{
    var client = new Client();
    NullSafeSet("test", s => client.Name = s);
    Debug.Assert(person.Name == "test");

    NullSafeSet("", s => client.Name = s);
    Debug.Assert(person.Name == "test");

    NullSafeSet(null, s => client.Name = s);
    Debug.Assert(person.Name == "test");
}

void NullSafeSet(string value, Action<string> setter)
{
    if (!string.IsNullOrEmpty(value))
    {
        setter(value);
    }
}
Firo
  • 30,626
  • 4
  • 55
  • 94
13

If you want to get and set the property both, you can use this in C#7:

GetString(
    inputString,
    (() => client.WorkPhone, x => client.WorkPhone = x))

void GetString(string inValue, (Func<string> get, Action<string> set) outValue)
{
    if (!string.IsNullOrEmpty(outValue.get()))
    {
        outValue.set(inValue);
    }
}
Erik
  • 5,355
  • 25
  • 39
Pellet
  • 2,254
  • 1
  • 28
  • 20
4

This is covered in section 7.4.1 of the C# language spec. Only a variable-reference can be passed as a ref or out parameter in an argument list. A property does not qualify as a variable reference and hence cannot be used.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
4

Just a little expansion to Nathan's Linq Expression solution. Use multi generic param so that the property doesn't limited to string.

void GetString<TClass, TProperty>(string input, TClass outObj, Expression<Func<TClass, TProperty>> outExpr)
{
    if (!string.IsNullOrEmpty(input))
    {
        var expr = (MemberExpression) outExpr.Body;
        var prop = (PropertyInfo) expr.Member;
        if (!prop.GetValue(outObj).Equals(input))
        {
            prop.SetValue(outObj, input, null);
        }
    }
}
g t
  • 7,287
  • 7
  • 50
  • 85
Zick Zhang
  • 41
  • 2
4

Inspired by Sven's expression tree solution that wraps the property in a generic 'accessor' class as a workaround for c#'s lack of native 'pass property by reference' support. Below is a simplified version that

  • doesn't rely on reflection
  • removes unnecessary custom getter instance
  • handles properties and fields the same
    using System;
    using System.Linq.Expressions;

    namespace Utils;
    
    public class Accessor<T>
    {
        public Accessor(Expression<Func<T>> expression)
        {
            if (expression.Body is not MemberExpression memberExpression)
                throw new ArgumentException("expression must return a field or property");
            var parameterExpression = Expression.Parameter(typeof(T));

            _setter = Expression.Lambda<Action<T>>(Expression.Assign(memberExpression, parameterExpression), parameterExpression).Compile();
            _getter = expression.Compile();
        }

        public void Set(T value) => _setter(value);
        public T Get() => _getter();

        private readonly Action<T> _setter;
        private readonly Func<T> _getter;
    }

Usage..

using System;
using NUnit.Framework;

namespace Utils.Tests;

public class AccessorTest
{
    private class TestClass
    {
        public string Property { get; set; }
    }

    [Test]
    public void Test()
    {
        var testClass = new TestClass { Property = "a" };

        var accessor = new Accessor<string>(() => testClass.Property);
        Assert.That(accessor.Get(), Is.EqualTo("a"));

        accessor.Set("b");
        Assert.That(testClass.Property, Is.EqualTo("b"));
        Assert.That(accessor.Get(), Is.EqualTo("b"));
    }
}
stoj
  • 1,116
  • 1
  • 14
  • 25
3

Another trick not yet mentioned is to have the class which implements a property (e.g. Foo of type Bar) also define a delegate delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); and implement a method ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1) (and possibly versions for two and three "extra parameters" as well) which will pass its internal representation of Foo to the supplied procedure as a ref parameter. This has a couple of big advantages over other methods of working with the property:

  1. The property is updated "in place"; if the property is of a type that's compatible with `Interlocked` methods, or if it is a struct with exposed fields of such types, the `Interlocked` methods may be used to perform atomic updates to the property.
  2. If the property is an exposed-field structure, the fields of the structure may be modified without having to make any redundant copies of it.
  3. If the `ActByRef` method passes one or more `ref` parameters through from its caller to the supplied delegate, it may be possible to use a singleton or static delegate, thus avoiding the need to create closures or delegates at run-time.
  4. The property knows when it is being "worked with". While it is always necessary to use caution executing external code while holding a lock, if one can trust callers not to do too do anything in their callback that might require another lock, it may be practical to have the method guard the property access with a lock, such that updates which aren't compatible with `CompareExchange` could still be performed quasi-atomically.

Passing things be ref is an excellent pattern; too bad it's not used more.

supercat
  • 77,689
  • 9
  • 166
  • 211
3

This is not possible. You could say

Client.WorkPhone = GetString(inputString, Client.WorkPhone);

where WorkPhone is a writeable string property and the definition of GetString is changed to

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

This will have the same semantics that you seem to be trying for.

This isn't possible because a property is really a pair of methods in disguise. Each property makes available getters and setters that are accessible via field-like syntax. When you attempt to call GetString as you've proposed, what you're passing in is a value and not a variable. The value that you are passing in is that returned from the getter get_WorkPhone.

g t
  • 7,287
  • 7
  • 50
  • 85
jason
  • 236,483
  • 35
  • 423
  • 525
2

Properties cannot be passed by reference ? Make it a field then, and use the property to reference it publicly:

public class MyClass
{
    public class MyStuff
    {
        string foo { get; set; }
    }

    private ObservableCollection<MyStuff> _collection;

    public ObservableCollection<MyStuff> Items { get { return _collection; } }

    public MyClass()
    {
        _collection = new ObservableCollection<MyStuff>();
        this.LoadMyCollectionByRef<MyStuff>(ref _collection);
    }

    public void LoadMyCollectionByRef<T>(ref ObservableCollection<T> objects_collection)
    {
        // Load refered collection
    }
}
macedo123
  • 84
  • 1
  • 2
1

What you could try to do is create an object to hold the property value. That way you could pass the object and still have access to the property inside.

1

The accepted answer is good if that function is in your code and you can modify it. But sometimes you have to use an object and a function from some external library and you can't change the property and function definition. Then you can just use a temporary variable.

var phone = Client.WorkPhone;
GetString(input, ref phone);
Client.WorkPhone = phone;
palota
  • 465
  • 4
  • 8
1

To vote on this issue, here is one active suggestion of how this could be added to the language. I'm not saying this is the best way to do this (at all), feel free to put out your own suggestion. But allowing properties to be passed by ref like Visual Basic already can do would hugely help simplify some code, and quite often!

https://github.com/dotnet/csharplang/issues/1235

Nicholas Petersen
  • 9,104
  • 7
  • 59
  • 69
0

You can't ref a property, but if your functions need both get and set access you can pass around an instance of a class with a property defined:

public class Property<T>
{
    public delegate T Get();
    public delegate void Set(T value);
    private Get get;
    private Set set;
    public T Value {
        get {
            return get();
        }
        set {
            set(value);
        }
    }
    public Property(Get get, Set set) {
        this.get = get;
        this.set = set;
    }
}

Example:

class Client
{
    private string workPhone; // this could still be a public property if desired
    public readonly Property<string> WorkPhone; // this could be created outside Client if using a regular public property
    public int AreaCode { get; set; }
    public Client() {
        WorkPhone = new Property<string>(
            delegate () { return workPhone; },
            delegate (string value) { workPhone = value; });
    }
}
class Usage
{
    public void PrependAreaCode(Property<string> phone, int areaCode) {
        phone.Value = areaCode.ToString() + "-" + phone.Value;
    }
    public void PrepareClientInfo(Client client) {
        PrependAreaCode(client.WorkPhone, client.AreaCode);
    }
}
chess123mate
  • 111
  • 1
  • 7
0

It seems that you are needing to impose a business rule constraint on that field, while at the same time wanting to keep your code as DRY as possible.

It is achievable and also preserves your domain semantics by implementing a full property on that field and using your re-usable method:

public class Client
{
    private string workPhone;

    public string WorkPhone
    {
        get => workPhone;
        set => SafeSetString(ref workPhone, value);
    }

    private void SafeSetString(ref string target, string source)
    {
        if (!string.IsNullOrEmpty(source))
        {
            target = source;
        }
    }
}

The SafeSetString method can be placed in a Utilities class or wherever it makes sense.

warren.sentient
  • 513
  • 6
  • 10
-1

Yes, you can't pass a property but you can convert your property to a property with backing field and do something like this.

public class SomeClass 
{
  private List<int> _myList;
  public List<int> MyList
  { 
    get => return _myList;
    set => _myList = value;
  }
  public ref List<int> GetMyListByRef()
  {
    return ref _myList;
  }
}

but there are better solutions like action delegate etc.

knile
  • 318
  • 3
  • 15