Passer des propriétés par référence en C #

J’essaie de faire ce qui suit:

GetSsortingng( inputSsortingng, ref Client.WorkPhone) private void GetSsortingng(ssortingng inValue, ref ssortingng outValue) { if (!ssortingng.IsNullOrEmpty(inValue)) { outValue = inValue; } } 

Cela me donne une erreur de compilation. Je pense que c’est assez clair ce que j’essaie de réaliser. En gros, je veux que GetSsortingng copie le contenu d’une chaîne d’entrée dans la propriété WorkPhone du Client .

Est-il possible de passer une propriété par référence?

Les propriétés ne peuvent pas être transmises par référence. Voici quelques façons de contourner cette limitation.

1. Valeur de retour

 ssortingng GetSsortingng(ssortingng input, ssortingng output) { if (!ssortingng.IsNullOrEmpty(input)) { return input; } return output; } void Main() { var person = new Person(); person.Name = GetSsortingng("test", person.Name); Debug.Assert(person.Name == "test"); } 

2. Délégué

 void GetSsortingng(ssortingng input, Action setOutput) { if (!ssortingng.IsNullOrEmpty(input)) { setOutput(input); } } void Main() { var person = new Person(); GetSsortingng("test", value => person.Name = value); Debug.Assert(person.Name == "test"); } 

3. Expression LINQ

 void GetSsortingng(ssortingng input, T target, Expression> outExpr) { if (!ssortingng.IsNullOrEmpty(input)) { var expr = (MemberExpression) outExpr.Body; var prop = (PropertyInfo) expr.Member; prop.SetValue(target, input, null); } } void Main() { var person = new Person(); GetSsortingng("test", person, x => x.Name); Debug.Assert(person.Name == "test"); } 

4. reflection

 void GetSsortingng(ssortingng input, object target, ssortingng propertyName) { if (!ssortingng.IsNullOrEmpty(input)) { prop = target.GetType().GetProperty(propertyName); prop.SetValue(target, input); } } void Main() { var person = new Person(); GetSsortingng("test", person, nameof(Person.Name)); Debug.Assert(person.Name == "test"); } 

sans dupliquer la propriété

 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(ssortingng value, Action setter) { if (!ssortingng.IsNullOrEmpty(value)) { setter(value); } } 

J’ai écrit un wrapper en utilisant la variante ExpressionTree et c # 7 (si quelqu’un est intéressé):

 public class Accessor { private Action Setter; private Func Getter; public Accessor(Expression> 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>(Expression.Call(instanceExpression, propertyInfo.GetSetMethod(), parameter), parameter).Comstack(); Getter = Expression.Lambda>(Expression.Call(instanceExpression, propertyInfo.GetGetMethod())).Comstack(); } else if (memberExpression.Member is FieldInfo fieldInfo) { Setter = Expression.Lambda>(Expression.Assign(memberExpression, parameter), parameter).Comstack(); Getter = Expression.Lambda>(Expression.Field(instanceExpression,fieldInfo)).Comstack(); } } public void Set(T value) => Setter(value); public T Get() => Getter(); } 

Et l’utiliser comme:

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

Une autre astuce non encore mentionnée est d’avoir la classe qui implémente une propriété (par exemple Foo de type Bar ) définissant également un délégué delegate void ActByRef(ref T1 p1, ref T2 p2); et implémenter une méthode ActOnFoo(ref Bar it, ActByRef proc, ref TX1 extraParam1) (et éventuellement des versions pour deux et trois “parameters supplémentaires” également) qui passeront sa représentation interne de Foo au procédure fournie en tant que paramètre ref . Cela a quelques gros avantages par rapport aux autres méthodes de travail avec la propriété:

  1. La propriété est mise à jour “en place”; Si la propriété est d’un type compatible avec les méthodes `Interlocked`, ou si c’est une structure avec des champs exposés de tels types, les méthodes` Interlocked` peuvent être utilisées pour effectuer des mises à jour atomiques sur la propriété.
  2. Si la propriété est une structure de champ exposé, les champs de la structure peuvent être modifiés sans avoir à en faire de copies redondantes.
  3. Si la méthode `ActByRef` transfère un ou plusieurs parameters` ref` de son appelant au délégué fourni, il est possible d’utiliser un délégué singleton ou statique, évitant ainsi de créer des fermetures ou des delegates au moment de l’exécution.
  4. La propriété sait quand il est “travaillé avec”. Bien qu’il soit toujours nécessaire de faire preuve de prudence lors de l’exécution d’un code externe tout en maintenant un verrou, si les appelants ne font rien dans leur callback pouvant nécessiter un autre verrou, il peut être pratique lock, de sorte que les mises à jour qui ne sont pas compatibles avec CompareExchange puissent toujours être effectuées de manière quasi atomique.

Faire passer les choses est un excellent modèle; dommage qu’il ne soit plus utilisé.

Ceci est couvert dans la section 7.4.1 de la spécification du langage C #. Seule une référence de variable peut être transmise en tant que paramètre ref ou out dans une liste d’arguments. Une propriété ne peut pas être considérée comme une référence de variable et ne peut donc pas être utilisée.

Ce n’est pas possible. Tu pourrais dire

 Client.WorkPhone = GetSsortingng(inputSsortingng, Client.WorkPhone); 

WorkPhone est une propriété de ssortingng WorkPhone écriture et la définition de GetSsortingng est modifiée en

 private ssortingng GetSsortingng(ssortingng input, ssortingng current) { if (!ssortingng.IsNullOrEmpty(input)) { return input; } return current; } 

Cela aura la même sémantique que vous semblez essayer.

Ce n’est pas possible car une propriété est vraiment une paire de méthodes déguisées. Chaque propriété met à disposition des getters et des setters accessibles via une syntaxe de type champ. Lorsque vous tentez d’appeler GetSsortingng comme vous l’avez proposé, ce que vous transmettez est une valeur et non une variable. La valeur que vous transmettez est celle renvoyée par getter get_WorkPhone .

Juste une petite extension de la solution Linq Expression de Nathan . Utilisez plusieurs parameters génériques pour que la propriété ne soit pas limitée à la chaîne.

 void GetSsortingng(ssortingng input, TClass outObj, Expression> outExpr) { if (!ssortingng.IsNullOrEmpty(input)) { var expr = (MemberExpression) outExpr.Body; var prop = (PropertyInfo) expr.Member; if (!prop.GetValue(outObj).Equals(input)) { prop.SetValue(outObj, input, null); } } } 

Ce que vous pouvez essayer de faire est de créer un object pour contenir la valeur de la propriété. De cette façon, vous pourriez passer l’object et toujours avoir access à la propriété à l’intérieur.

Si vous voulez obtenir et définir la propriété à la fois, vous pouvez l’utiliser dans C # 7:

 GetSsortingng( inputSsortingng, (() => client.WorkPhone, x => client.WorkPhone = x)) void GetSsortingng(ssortingng inValue, (Func get, Action set) outValue) { if (!ssortingng.IsNullOrEmpty(outValue)) { outValue.set(inValue); } } 

Vous ne pouvez pas ref une propriété, mais si vos fonctions nécessitent à la fois get access get et set vous pouvez faire circuler une instance d’une classe avec une propriété définie:

 public class Property { 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; } } 

Exemple:

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