在C#中通过引用传递属性

我正在努力做到以下几点:

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

这给我一个编译错误。 我想它很清楚我想要达到什么目的。 基本上我希望GetString将inputstring的内容复制到ClientWorkPhone属性。

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

属性不能通过引用传递。 以下是一些可以解决此限制的方法。

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. LINQexpression式

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

没有重复的财产

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

我使用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"); 

这在C#语言规范的第7.4.1节中有介绍。 只有一个variables引用可以作为参数列表中的ref或outparameter passing。 一个属性没有资格作为variables引用,因此不能使用。

还没有提到的另一个技巧是让实现一个属性的类(例如Bartypes的Foo )也定义一个委托delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); 并实现一个方法ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1) (也可能是两个和三个“额外参数”的版本),它将把它的内部表示Foo传递给提供的过程作为ref参数。 与其他方法相比,这有两个很大的优势:

  1. 该物业更新“到位”; 如果该属性的types与“互锁”方法兼容,或者如果它是具有这种types的公开的字段的结构,则可以使用“互锁”方法对属性执行primefaces更新。
  2. 如果该属性是暴露字段结构,则可以修改该结构的字段而不必制作任何冗余副本。
  3. 如果`ActByRef`方法将一个或多个`ref`参数从其调用者传递给所提供的委托,则可以使用单例或静态委托,从而避免在运行时创build闭包或委托。
  4. 该财产知道什么时候正在“工作”。 尽pipe在执行外部代码时总是需要谨慎,但如果可以相信呼叫者不要在callback中做任何事情,而这些操作可能需要另一个锁,那么使用方法来保护属性访问是可行的locking,使得与“CompareExchange”不兼容的更新仍然可以准自动执行。

传递事物是一个很好的模式; 太糟糕了,没用多了。

这不可能。 你可以说

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

WorkPhone是可写string属性, GetString的定义更改为

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

这将具有您似乎正在尝试的相同的语义。

这是不可能的,因为一个财产真的是变相的一对方法。 每个属性都提供了可以通过类似字段的语法访问的getter和setter。 当您尝试按照您的build议调用GetString ,您传递的是一个值而不是一个variables。 你传入的值是从getter get_WorkPhone返回的get_WorkPhone

只是Nathan的Linq Expression解决scheme的一点点扩展。 使用多个通用参数,以便该属性不限于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); } } } 

你可以尝试做的是创build一个对象来保存属性值。 这样你可以传递对象,并仍然可以访问里面的属性。

你不能ref一个属性,但是如果你的函数同时需要getset访问权限,你可以传递一个类的实例,并定义一个属性:

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

例:

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