我想做的是:

GetString(
    inputString,
    ref Client.WorkPhone)

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

这给了我一个编译错误。我想我想达到的目的已经很清楚了。基本上我想要GetString复制输入字符串的内容到客户端的WorkPhone属性。

是否可以通过引用传递属性?


当前回答

属性不能通过引用传递。这里有一些方法可以绕过这个限制。

1. 返回值

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. 委托

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表达式

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. 反射

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

其他回答

这是不可能的。你可以说

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

其中WorkPhone是一个可写的字符串属性和GetString的定义被更改为

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

这将具有与您所尝试的相同的语义。

这是不可能的,因为属性实际上是一对伪装的方法。每个属性都提供了可通过类字段语法访问的getter和setter。当您尝试调用GetString时,您传递的是一个值而不是一个变量。您传入的值是从getter get_WorkPhone返回的。

我用ExpressionTree变体和c#7写了一个包装器(如果有人感兴趣的话):

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

像这样使用它:

var accessor = new Accessor<string>(() => myClient.WorkPhone);
accessor.Set("12345");
Assert.Equal(accessor.Get(), "12345");

只是对内森的Linq表达式解决方案的一点扩展。使用多泛型参数,使属性不限于字符串。

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

是的,你不能传递一个属性,但你可以将你的属性转换为带有支持字段的属性,并做类似的事情。

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

但有更好的解决方案,比如动作委托等。

如果函数在您的代码中,并且您可以修改它,那么接受的答案是好的。但有时你必须使用某个外部库中的对象和函数,你不能改变属性和函数定义。然后你可以使用一个临时变量。

var phone = Client.WorkPhone;
GetString(input, ref phone);
Client.WorkPhone = phone;