Pourquoi ne puis-je pas passer une propriété ou un indexeur en tant que paramètre ref lorsque le réflecteur .NET indique que cela est fait dans le .NET Framework?

StackOverflow https://stackoverflow.com/questions/1810785

  •  05-07-2019
  •  | 
  •  

Question

D'accord, je vais couper et coller depuis le réflecteur .NET pour montrer ce que j'essaie de faire:

public override void UpdateUser(MembershipUser user)
{
    //A bunch of irrelevant code...

    SecUtility.CheckParameter(ref user.UserName, true, true, true, 0x100, "UserName");

    //More irrelevant code...
}

Cette ligne de code provient directement de System.Web.Security.SqlMembershipProvider.UpdateUser (System.Web.dll v2.0.50727) dans le .NET Framework.

Le paramètre SecUtility.CheckParameter nécessite une valeur de référence en tant que premier paramètre, auquel il transmet une propriété de l'utilisateur transmise en tant qu'argument.

La définition du code CheckParameter est la suivante:

internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
{
    //Code omitted for brevity
}

Tout ce que vous faites est logique - sur le papier ... alors je coche un petit prototype rapide pour un endroit où j'aimerais utiliser quelque chose de similaire:

public class DummyClass
{
    public string ClassName{ get; set; }
}

public class Program
{
    private static DoSomething(ref string value)
    {
        //Do something with the value passed in
    }

    public static Main(string[] args)
    {
        DummyClass x = new DummyClass() { ClassName = "Hello World" };

        DoSomething(ref x.ClassName); //This line has a red squiggly underline 
                                      //under x.ClassName indicating the 
                                      //error provided below.
    }
}

Ce code ne sera pas compilé - l'erreur indique:

"A property or indexer may not be passed as an out or ref parameter"

Très bien ... mais pourquoi mon code ne me permet-il pas de faire quelque chose qui semble être dans la base de code .NET Framework? Est-ce une erreur dans la façon dont .NET Reflector interprète la DLL ou est-ce une erreur dans la façon dont j'interprète leur code?

Était-ce utile?

La solution

Je pense que c'est une mauvaise interprétation de Reflector. En fait, si vous écrivez votre code comme ceci:

static void Main(string[] args)
{
    DummyClass x = new DummyClass();
    string username = x.ClassName;
    DoSomething(ref username);
}

et compilez-le en mode Release, vous le verrez dans Reflector:

static void Main(string[] args)
{
    DummyClass x = new DummyClass();
    DoSomething(ref x.ClassName);
}

N'oubliez pas que le compilateur C # ne produit pas de code C # mais IL, de sorte que ce que vous voyez dans Reflector n'est pas toujours la réalité. Donc, pour bien comprendre ce qui se passe sous le capot, vous pouvez regarder le vrai code produit par le compilateur:

L_000f: callvirt instance string System.Web.Security.MembershipUser::get_UserName()
L_0014: stloc.0 
L_0015: ldloca.s str
L_0017: ldc.i4.1 
L_0018: ldc.i4.1 
L_0019: ldc.i4.1 
L_001a: ldc.i4 0x100
L_001f: ldstr "UserName"
L_0024: call void System.Web.Util.SecUtility::CheckParameter(string&, bool, bool, bool, int32, string)

Il est clair qu'une variable locale est utilisée.

Autres conseils

C'est un bug de réflecteur. Ce n'est pas vraiment en passant la propriété par référence.

Voici un code C # qui le reproduira.

using System;

class Person
{
    public string Name { get; set; }
}

class Test
{
    static void Main(){} // Just make it easier to compile

    static void Foo(Person p)
    {
        string tmp = p.Name;
        Bar(ref tmp);
    }

    static void Bar(ref string x)
    {
    }
}

Le réflecteur affiche ce code pour Foo :

private static void Foo(Person p)
{
    Bar(ref p.Name);
}

Non seulement ce C # non valide, mais il est trompeur - cela suggérerait que les modifications apportées à x dans Bar modifieraient en quelque sorte p.Name - où ce n'est pas le cas lorsque vous regardez le code C # d'origine.

Dans votre exemple d'origine, cela n'a même pas de sens puisque nom d'utilisateur est une propriété en lecture seule!

Essayez de définir la valeur de la propriété sur une variable avant de la transmettre à la fonction.

string myClassName = x.ClassName
DoSomething(ref myClassName);

Ce n’est pas la solution la plus élégante, mais elle devrait vous orienter dans la bonne direction. Comme Yuriy l’a dit dans son commentaire ci-dessus, cela tient probablement au fait que vous ne déclarez pas explicitement un get et un ensemble pour la propriété.

Lorsque vous transmettez une variable en tant que ref ou out, l'appel pointe en fait sur la mémoire où elle se trouve à l'origine. Ainsi, si vous êtes autorisé à transmettre la propriété en tant que référence, cela signifie que vous rendez le membre du groupe incohérent. C'est la raison derrière ce problème.

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