Question

Dans .NET 4 beta 2, il existe le nouvel espace de noms Numerics avec struct BigInteger . La documentation indique que c'est un type immuable, comme je l'aurais cru.

Mais l'opérateur de post-incrémentation ( ++ ) me laisse un peu perplexe. Cela semble définitivement transformer la valeur. La boucle While suivante fonctionne:

static BigInteger Factorial(BigInteger n)
{
    BigInteger result = BigInteger.One;
    BigInteger b = BigInteger.One;

    while (b <= n)
    {
        result = result * b;
        b++;  // immutable ?
    }
    return result;
}

Voici ce que MSDN a à dire à propos de l'opérateur Increment:

  

Parce que les objets BigInteger sont   immuable, l'opérateur Increment   crée un nouvel objet BigInteger dont   la valeur est un de plus que le BigInteger   objet représenté par valeur.   Par conséquent, les appels répétés à incrémenter   peut être cher.

Tout va bien, j'aurais compris si je devais utiliser b = b ++ mais apparemment, ++ suffit à lui seul à changer une valeur.

Avez-vous des idées?

Était-ce utile?

La solution

Les opérateurs ++ et - sont implémentés en termes d'opérateurs normaux + et - , alors en réalité:

b++;

est équivalent à:

var temp = b;
b = b + 1;
<use temp for the expression where b++ was located>

Maintenant, commenté, cela pourrait sembler rompre l’immuabilité, mais ce n’est pas le cas.

Vous devriez plutôt regarder ce code comme ceci:

var temp = b;
b = BigInteger.op_Add(b, 1); // constructs a new BigInteger value
<use temp ...>

Cela laissera deux objets en mémoire, la valeur BigInteger d'origine et le nouvel objet, à présent référencé par b. Vous pouvez facilement vérifier que c'est ce qui se passe avec le code suivant:

var x = b;
b++;
// now inspect the contents of x and b, and you'll notice that they differ

Donc, l'objet d'origine n'a pas changé, donc il ne rompt pas l'immutabilité, et pour répondre à la nouvelle partie de la question, cela devrait être thread-safe.

C'est la même chose qui arrive aux chaînes:

String s1 = s2;
s2 += "More";
// now inspect s1 and s2, they will differ

Autres conseils

Puisque BigInteger est immuable, b ++ sera simplement équivalent à:

BigInteger temp=b;
b=temp+1;

Après cette opération, temp est recyclé par le CPG et la mémoire est libérée.

BigInteger b = BigInteger.One;

b++;  // immutable ?

Dans votre exemple, b est une variable, qui n'est qu'un emplacement de mémoire dans le cadre de pile de la méthode actuelle. Il est initialisé à One et b ++ prend b, crée un nouveau BigInteger (avec la valeur incrémentée) et le renvoie. la variable b a maintenant l’état du nouveau BigInteger renvoyé.

Pour être honnête, l’immuabilité en tant que concept est beaucoup plus claire s’agissant des types de référence, car il existe un objet sur le tas dont l’état interne ne change jamais. Ainsi, quand une opération / méthode retourne un nouvel objet avec un état différent, c’est évident. (Par exemple, vous pouvez effectuer une vérification d'égalité de référence d'objet avec object.ReferenceEquals (objet, objet).

Pour les types de valeur, il n'y a pas d'objet sur le tas, il n'y a que l'emplacement dans la mémoire qui contient les bits qui sont la valeur.

OK, mais qu'en est-il de l'opérateur de négation unaire défini sur BigInteger:

public static BigInteger operator -(BigInteger value)
{
    value._sign = -value._sign;
    return value;
}

il semble casser le modèle d’immutabilité et muter directement l’objet BigInteger. Donc

b = -b;

change réellement un BigInteger existant à la place sans renvoyer un nouvel objet.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top