web-dev-qa-db-fra.com

Pourquoi C # ne prend-il pas en charge le retour des références?

J'ai lu que .NET prend en charge le retour de références, mais pas C #. Y a-t-il une raison particulière? Pourquoi je ne peux pas faire quelque chose comme:

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 
139
Tom Sarduy

Cette question a fait l'objet de mon blog du 23 juin 2011 . Merci pour la grande question!

L'équipe C # envisage cela pour C # 7. Voir https://github.com/dotnet/roslyn/issues/52 pour plus de détails.

MISE À JOUR: La fonctionnalité est arrivée en C # 7!


Vous avez raison; .NET prend en charge les méthodes qui renvoient les références gérées aux variables. .NET prend également en charge les variables locales qui contiennent des références gérées à d'autres variables. (Notez cependant que .NET ne prend pas en charge les champs ou les tableaux qui contiennent les références gérées à d'autres variables car cela complique excessivement l'histoire de la récupération de place. De plus, les types de "référence gérée aux variables" ne sont pas non convertibles en objet , et peuvent donc ne pas être utilisé comme arguments de type pour des types ou des méthodes génériques.)

Le commentateur "RPM1984" a demandé pour une raison quelconque une citation pour ce fait. RPM1984 Je vous encourage à lire la spécification CLI Partition I Section 8.2.1.1, "Pointeurs gérés et types connexes" pour plus d'informations sur cette fonctionnalité de .NET.

Il est tout à fait possible de créer une version de C # qui prend en charge ces deux fonctionnalités. Vous pourriez alors faire des choses comme

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

puis appelez-le avec

int a = 123;
int b = 456; 
ref int c = ref Max(ref a, ref b); 
c += 100;
Console.WriteLine(b); // 556!

Je sais empiriquement qu'il est possible de construire une version de C # qui supporte ces fonctionnalités parce que je l'ai fait. Les programmeurs avancés, en particulier les personnes qui portent du code C++ non managé, nous demandent souvent plus de capacités de type C++ pour faire des choses avec des références sans avoir à tirer le gros coup de l'aide réelle des pointeurs et de l'épinglage de la mémoire partout. En utilisant des références gérées, vous bénéficiez de ces avantages sans payer le coût de gâcher vos performances de collecte des ordures.

Nous avons envisagé cette fonctionnalité et en avons implémenté suffisamment pour montrer aux autres équipes internes leurs commentaires. Cependant, à l'heure actuelle, sur la base de nos recherches , nous pensons que la fonctionnalité n'a pas un attrait suffisamment large ou des cas d'utilisation convaincants pour en faire une véritable fonctionnalité linguistique prise en charge . Nous avons d'autres priorités plus élevées et un temps et des efforts limités disponibles, nous n'allons donc pas utiliser cette fonctionnalité de sitôt.

De plus, le faire correctement nécessiterait quelques modifications du CLR. À l'heure actuelle, le CLR traite les méthodes de retour de référence comme légal mais non vérifiable parce que nous n'avons pas de détecteur qui détecte cette situation:

ref int M1(ref int x)
{
    return ref x;
}

ref int M2()
{
    int y = 123;
    return ref M1(ref y); // Trouble!
}

int M3()
{
    ref int z = ref M2();
    return z;
}

M3 renvoie le contenu de la variable locale de M2, mais la durée de vie de cette variable est terminée! Il est possible d'écrire un détecteur qui détermine les utilisations des renvois de référence qui font clairement pas violer la sécurité de la pile. Ce que nous ferions, c'est écrire un tel détecteur, et si le détecteur ne pouvait pas prouver la sécurité de la pile, alors nous ne permettrions pas l'utilisation de retours de référence dans cette partie du programme. Ce n'est pas un énorme travail de développement de le faire, mais c'est une lourde charge pour les équipes de test de s'assurer que nous avons vraiment tous les cas. C'est juste une autre chose qui augmente le coût de la fonctionnalité au point où, actuellement, les avantages ne l'emportent pas sur les coûts.

Si vous pouvez me décrire pourquoi vous voulez cette fonctionnalité, j'apprécierais vraiment cela . Plus nous aurons d'informations de vrais clients sur les raisons pour lesquelles ils le souhaitent, plus il sera probable qu'un jour il soit intégré au produit. C'est une petite fonctionnalité mignonne et j'aimerais pouvoir l'obtenir auprès des clients d'une manière ou d'une autre s'il y a suffisamment d'intérêt.

(Voir aussi les questions connexes Est-il possible de renvoyer une référence à une variable en C #? et Puis-je utiliser une référence à l'intérieur d'une fonction C # comme C++? )

187
Eric Lippert

Vous parlez de méthodes qui renvoient une référence à un type de valeur. Le seul exemple intégré en C # que je connaisse est l'accesseur de tableau d'un type de valeur:

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

et maintenant créer un tableau de cette structure:

var points = new Point[10];
points[0].X = 1;
points[0].Y = 2;

Dans ce cas points[0], le tableau indexeur, renvoie une référence à struct. Il est impossible d'écrire votre indexeur propre (par exemple pour une collection personnalisée), qui a le même comportement "retourner une référence".

Je n'ai pas conçu le langage C # donc je ne connais pas tout le raisonnement derrière ne pas le soutenir, mais je pense que la réponse courte pourrait être: nous pouvons très bien nous entendre sans lui.

22
Rick Sladkey

C # 7.0 prend en charge le renvoi de références. Voir ma réponse ici .

1
CodingYoshi

Vous pouvez toujours faire quelque chose comme:

public delegate void MyByRefConsumer<T>(ref T val);

public void DoSomethingWithValueType(MyByRefConsumer<int> c)
{
        int x = 2;
        c(ref x);
        //Handle potentially changed x...
}
1
Eladian