293 votes

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

J'essaie de faire les choses suivantes:

 GetString(
    inputString,
    ref Client.WorkPhone)

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

Cela me donne une erreur de compilation. Je pense que c'est assez clair ce que j'essaie de réaliser. Fondamentalement , je veux GetString pour copier le contenu d'une chaîne d'entrée à l' WorkPhone propriété de Client .

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

520voto

Nathan Baulch Points 7994

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

 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. Délégués

 void GetString(string input, Func<string> getOutput, Action<string> setOutput)
{
    if (!string.IsNullOrEmpty(input))
    {
        setOutput(input);
    }
}

void Main()
{
    var person = new Person();
    GetString("test", () => person.Name, value => person.Name = value);
    Debug.Assert(person.Name == "test");
}
 

3. Expressions Linq

 void GetString<T>(string input, T outObj, Expression<Func<T, string>> outExpr)
{
    if (!string.IsNullOrEmpty(input))
    {
        var expr = (MemberExpression) outExpr.Body;
        var prop = (PropertyInfo) expr.Member;
        prop.SetValue(outObj, input, null);
    }
}

void Main()
{
    var person = new Person();
    GetString("test", person, x => x.Name);
    Debug.Assert(person.Name == "test");
}
 

34voto

Firo Points 21003

sans la duplication de 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(string value, Action<string> setter)
{
    if (!string.IsNullOrEmpty(value))
    {
        setter(value);
    }
}
 

4voto

JaredPar Points 333733

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 constitue pas une référence de variable et ne peut donc pas être utilisée.

3voto

supercat Points 25534

Un autre truc pas encore mentionné, c'est d'avoir la classe qui implémente une propriété (par exemple, Foo de type Bar) également définir un délégué delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); et de mettre en œuvre une méthode ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1) (et, éventuellement, des versions pour les deux et trois "paramètres supplémentaires") qui va passer sa représentation interne d' Foo à la procédure en tant que ref paramètre. Cela a un couple de grands avantages sur les autres méthodes de travail avec la propriété:

  1. La propriété est mise à jour "à la place"; si la propriété est d'un type compatible avec "Interloqué" des méthodes, ou si c'est une struct avec exposé des champs de ces types, le "Interloqué" méthodes peuvent être utilisées pour effectuer les mises à jour atomiques à la propriété.
  2. Si la propriété est un exposé-structure du champ, les champs de la structure peut être modifié sans avoir à effectuer des copies redondantes.
  3. Si le "ActByRef la méthode des" passes un ou plusieurs " ref " paramètres par le biais de son appelant à la délégué fourni, il peut être possible d'utiliser un singleton ou délégué statique, évitant ainsi la nécessité de créer des fermetures ou des délégués au moment de l'exécution.
  4. La propriété sait quand il est "travaillé". Alors qu'il est toujours nécessaire de faire preuve de prudence l'exécution de code externe tout en maintenant un verrou, si l'on peut faire confiance aux appelants de ne pas faire trop n'importe quoi dans leur rappel qui peut nécessiter un autre verrou, il peut être pratique d'avoir de la méthode de garde de l'accès à la propriété avec une serrure, tels que les mises à jour qui ne sont pas compatibles avec "CompareExchange" pourrait encore être fait de façon quasi-automatiquement.

Les choses qui passent être ref est un excellent modèle; trop mauvais, il ne sert plus.

3voto

Jason Points 125291

Ce n'est pas possible. On pourrait dire

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

WorkPhone est un inscriptible string de la propriété et de la définition de l' GetString est modifié pour

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

Cela permettra d'avoir la même sémantique que vous semblez essayer pour.

Ce n'est pas possible parce qu'un bien est vraiment une paire de méthodes dans le déguisement. Chaque propriété met à la disposition des getters et setters qui sont accessibles via le champ de syntaxe. Lorsque vous tentez d'appeler GetString comme vous l'avez proposé, ce que vous êtes de passage dans est une valeur et non une variable. La valeur que vous êtes de passage dans l'est qui a renvoyé à partir de la lecture get_WorkPhone.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X