web-dev-qa-db-fra.com

Propriété C # et paramètre ref, pourquoi pas de sucre?

Je viens de rencontrer ce message d'erreur en travaillant en C #

Une propriété ou un indexeur ne peut pas être passé en tant que paramètre out ou ref

J'ai su ce qui a causé cela et j'ai fait la solution rapide de créer une variable locale du type correct, en appelant la fonction avec elle en tant que paramètre out/ref, puis en la réaffectant à la propriété:

RefFn(ref obj.prop);

se transforme en

{
    var t = obj.prop;
    RefFn(ref t);
    obj.prop = t;
}

De toute évidence, cela échouerait si la propriété ne prend pas en charge get et set dans le contexte actuel.

Pourquoi C # ne fait-il pas ça pour moi?


Les seuls cas où je peux penser où cela pourrait causer des problèmes sont:

  • filetage
  • des exceptions

Pour le filetage, cette transformation affecte le moment où les écritures se produisent (après l'appel de fonction par rapport à l'appel de fonction), mais je soupçonne plutôt que tout code qui compte compterait peu de sympathie lorsqu'il se casse.

Pour les exceptions, la préoccupation serait; que se passe-t-il si la fonction attribue à l'un des plusieurs paramètres ref que jette? Toute solution triviale entraînerait l'affectation de tout ou partie des paramètres lorsque certains devraient l'être et d'autres ne devraient pas l'être. Encore une fois, je ne pense pas que ce serait une utilisation supportée de la langue.


Remarque: je comprends la mécanique de la raison pour laquelle ces messages d'erreur sont générés. Ce que je recherche, c'est la raison pour laquelle C # n'implémente pas automatiquement la solution de contournement triviale.

70
BCS

Juste pour info, C # 4.0 va avoir quelque chose comme ce sucre, mais seulement lors de l'appel de méthodes d'interopérabilité - en partie à cause de la propension pure de ref dans ce scénario . Je ne l'ai pas beaucoup testé (dans le CTP); nous devrons voir comment cela se déroule ...

11
Marc Gravell

Parce que vous passez le result de l'indexeur, qui est vraiment le résultat d'un appel de méthode. Il n'y a aucune garantie que la propriété de l'indexeur possède également un setter, et le passer par ref entraînerait une fausse sécurité de la part du développeur lorsqu'il pense que sa propriété va être définie sans que le setter soit appelé.

À un niveau plus technique, ref et out transmettent l'adresse mémoire de l'objet qui leur est transmis, et pour définir une propriété, vous devez appeler le setter, il n'y a donc aucune garantie que la propriété serait réellement modifiée, surtout lorsque le type de propriété est immuable. ref et out ne font pas que set la valeur au retour de la méthode, ils transmettent la référence mémoire réelle à l'objet lui-même.

32
David Morton

Les propriétés ne sont rien d'autre que du sucre syntaxique sur les méthodes Java style getX/setX. Cela n'a pas beaucoup de sens pour 'ref' sur une méthode. Dans votre cas, cela aurait du sens parce que vos propriétés sont simplement des zones de stubbing. Les propriétés ne doivent pas être simplement des stubs, donc le framework ne peut pas autoriser 'ref' sur les propriétés.

EDIT : Eh bien, la réponse simple est que le simple fait qu'un getter ou un setter de propriété puisse inclure bien plus qu'un simple champ de lecture/écriture le rend indésirable, sans oublier peut-être inattendu, pour permettre le type de sucre que vous proposez. Cela ne veut pas dire que je n'avais pas eu besoin de cette fonctionnalité auparavant, juste que je comprends pourquoi ils ne voudraient pas la fournir.

17
user7116

Vous pouvez utiliser des champs avec ref/out, mais pas de propriétés. La raison en est que les propriétés ne sont en fait qu'un raccourci de syntaxe pour des méthodes spéciales. Le compilateur traduit réellement les propriétés get/set en get_X et set_X méthodes car le CLR n'a pas de support immédiat pour les propriétés.

9
Brian Rasmussen

Ce ne serait pas thread-safe; si deux threads créent simultanément leurs propres copies de la valeur de la propriété et les transmettent aux fonctions en tant que paramètres ref, un seul d'entre eux se retrouve dans la propriété.

class Program
{
  static int PropertyX { get; set; }

  static void Main()
  {
    PropertyX = 0;

    // Sugared from: 
    // WaitCallback w = (o) => WaitAndIncrement(500, ref PropertyX);
    WaitCallback w = (o) => {
      int x1 = PropertyX;
      WaitAndIncrement(500, ref x1);
      PropertyX = x1;
    };
    // end sugar

    ThreadPool.QueueUserWorkItem(w);

    // Sugared from: 
    // WaitAndIncrement(1000, ref PropertyX);
    int x2 = PropertyX;      
    WaitAndIncrement(1000, ref x2);
    PropertyX = x2;
    // end sugar

    Console.WriteLine(PropertyX);
  }

  static void WaitAndIncrement(int wait, ref int i)
  {
    Thread.Sleep(wait);
    i++;
  }
}

PropertyX se termine par 1, alors qu'un champ ou une variable locale serait 2.

Cet exemple de code met également en évidence les difficultés introduites par des choses comme les méthodes anonymes lors de la demande au compilateur de faire des choses sucrées.

6
Mark Rendle

Lorsque vous passez ref/out en préfixe, cela signifie que vous passez un type de référence qui est stocké dans le tas.

Les propriétés sont des méthodes wrapper, pas des variables.

4
Igor Zelaya

La raison en est que C # ne prend pas en charge les propriétés "paramétrables" qui acceptent les paramètres passés par référence. Il est intéressant de noter que le CLR prend en charge cette fonctionnalité, mais pas C #.

4
Andrew Hare

Si vous demandez pourquoi le compilateur ne remplace pas le champ renvoyé par le getter de la propriété, c'est parce que le getter peut renvoyer une constante ou en lecture seule ou littérale ou quelque chose d'autre qui ne doit pas être réinitialisé ou écrasé.

1
BC.

Ce site semble avoir une solution pour vous. Je ne l'ai pas testé cependant, je ne peux donc pas garantir que cela fonctionnera. L'exemple semble utiliser la réflexion pour accéder aux fonctions get et set de la propriété. Ce n'est probablement pas une approche recommandée, mais cela pourrait accomplir ce que vous demandez.

http://www.codeproject.com/KB/cs/Passing_Properties_byref.aspx

0
regex