web-dev-qa-db-fra.com

En C #, pourquoi ne puis-je pas remplir une variable locale à l'aide de son adresse, puis utilisez la variable plus tard?

Considérez le code suivant:

private unsafe void Function()
{
    int length;

    // This line raises error CS1686, "Local 'length' or its members cannot have their address taken and be used inside an anonymous method or lambda expression".
    glGetProgramiv(1, GL_PROGRAM_BINARY_LENGTH, &length);

    FunctionWithLambda(() => Console.WriteLine(length));
}

private void FunctionWithLambda(Action callback)
{
    callback();
}

Notez que je prends l'adresse de length (une variable locale), puis utilisez la variable la variable elle-même ( non Son adresse) une lambda. Je comprends pourquoi une adresse de variable locale ne peut pas être utilisée dans une lambda directement (voir pourquoi ne pouvez-je pas passer l'adresse d'une variable à une fonction anonyme? , parmi Autres exemples), mais pourquoi ne puis-je pas utiliser le valeur de length une fois attribué (même si cette affectation arrive à utiliser le & opérateur)? La documentation officielle pour erreur CS1686 ( https://docs.microsoft.com/bs-latn-ba/dotnet/cshaarp/misc/cs1686 ) n'a pas clarifié cette confusion.

Mon hypothèse est que c'est simplement une limitation de la langue, mais je suis curieux s'il y a une raison technique sous-jacente, je manque. Notez également que je ne demande pas Comment travailler autour de ce problème (Je sais que je peux facilement copier length à une autre variable locale en premier).

6
Grimelios

Je suppose que la raison est simple: trop complexe à compiler.

Il y a 2 problèmes que le compilateur doit résoudre:

  1. Générer un concile pour la méthode anonyme.
  2. Synchroniser la valeur de la variable.

Supposons que les codes suivants sont valides.

unsafe void Function()
{
    int length = 1;
    void bar() => Console.WriteLine(length);
    bar();
    foo(&length);
    bar();
}

unsafe void foo(int* i) { (*i)++; }

Le résultat attendu est:

1
2

Pour résoudre le premier problème, c # générera une classe anonyme pour tenir la suppression.

Voici le pseudocode:

class _Anonymous
{
    public int _length;

    public void _bar() { Console.WriteLine(_length); }
}

unsafe void Function()
{
    int length = 1;
    var a = new _Anonymous { _length = length };
    a._bar();
    foo(&length);
    a._bar();
}

Pour résoudre le deuxième problème C # utilise le champ généré au lieu de la variable locale d'origine.

unsafe void Function()
{
    //int length = 1;
    var a = new _Anonymous { _length = 1 };
    a._bar();
    foo(&a._length);
    a._bar();
}

Ce sont toutes les œuvres qu'un compilateur peut faire. Mais jusqu'à présent, les codes ne fonctionneront toujours pas, nous avons besoin d'un bloc fixe supplémentaire.

unsafe void Function()
{
    var a = new _Anonymous { _length = 1 };
    a._bar();
    fixed (int* p = &a._length)
        foo(p);
    a._bar();
}

La limitation peut donc être enlevée avec un compilateur plus intelligent, mais les choses sont plus faciles si nous interdisons ce type de codes.

0
shingo