¿Por qué no puedo pasar una propiedad o un indexador como un parámetro de referencia cuando el reflector .NET muestra que se ha hecho en .NET Framework?

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

  •  05-07-2019
  •  | 
  •  

Pregunta

De acuerdo, cortaré y pegaré desde el reflector .NET para demostrar lo que estoy tratando de hacer:

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

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

    //More irrelevant code...
}

Esta línea de código sale directamente de System.Web.Security.SqlMembershipProvider.UpdateUser (System.Web.dll v2.0.50727) en .NET Framework.

El parámetro SecUtility.CheckParameter requiere un valor de referencia como primer parámetro, al que se pasa una propiedad del usuario que se pasa como argumento.

La definición del código CheckParameter es:

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

Todo lo que hace tiene sentido, en papel ... así que descubro un pequeño prototipo rápido para un lugar en el que me gustaría usar algo similar:

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.
    }
}

Este código no se compilará, el error se muestra como:

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

Bastante justo ... pero ¿por qué mi código no me permite hacer algo que parece estar en la base de código de .NET Framework? ¿Se trata de un error en la forma en que .NET Reflector interpreta la DLL o es un error en la forma en que interpreto su código?

¿Fue útil?

Solución

Creo que es una mala interpretación de Reflector. En realidad, si escribes tu código de esta manera:

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

y compílalo en el modo Release; verás esto en Reflector:

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

Recuerde que el compilador de C # no está produciendo el código de C # sino IL, por lo que lo que ve en Reflector no siempre es la realidad. Entonces, para comprender claramente lo que sucede debajo del capó, puede ver el código real producido por el compilador:

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)

Está claro que se utiliza una variable local.

Otros consejos

Es un error del reflector. Realmente no es pasar la propiedad por referencia.

Aquí hay un código C # que lo reproducirá.

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)
    {
    }
}

Reflector muestra este código para Foo :

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

No solo no es válido C #, sino que es engañoso: sugeriría que los cambios realizados en x dentro de Bar de alguna manera modificarían p.Name - donde ese no es el caso cuando miras el código C # original.

En su muestra original, tiene menos sentido, ya que UserName es una propiedad de solo lectura.

Intente establecer el valor de la propiedad en una variable antes de pasarla a la función.

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

No es la solución más elegante, pero debería apuntarle en la dirección correcta. Como Yuriy dijo en su comentario anterior, es probable que tenga algo que ver con el hecho de que no está declarando explícitamente un get y un conjunto para la propiedad.

Cuando pasa una variable como referencia o salida, la llamada en realidad apunta a la memoria donde se encuentra originalmente. Entonces, si se le permite pasar la propiedad como referencia, significa que está haciendo que el miembro de la clase sea inconsistente. Esta es la razón detrás de este problema.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top