2009-11-27 167 views
12

Bueno, voy a cortar y pegar desde el reflector .NET para demostrar lo que estoy tratando de hacer:¿Por qué no puedo pasar una propiedad o indizador como un parámetro ref cuando el reflector .NET muestra que está hecho en .NET Framework?

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 viene directamente de System.Web.Security.SqlMembershipProvider.UpdateUser (Sistema. Web.dll v2.0.50727) en .NET Framework.

El SecUtility.CheckParameter requiere un valor de referencia como primer parámetro, al que pasan una propiedad del usuario pasado 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 está haciendo tiene sentido - en el papel ... así que llamo un poco de prototipo rápido para un lugar 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á - muestra el error como:

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

Bastante ... pero ¿por qué mi código no me permite hacer algo que parece estar en la base de código de .NET Framework? ¿Es esto un error con la forma en que .NET Reflector está interpretando la DLL o es un error con la forma en que estoy interpretando su código?

+0

Usted está utilizando una propiedad automática. Cuando se compila, se convierte en dos funciones. Entonces el marco no puede pasar una referencia a dos funciones a otra cosa. –

+0

@Yuriy - eso sería comprensible. Sin embargo, en el código que me hizo notar este problema, estoy usando la clase * same * MembershipUser mencionada en el Framework * exactamente de la misma manera *: ¿cómo es posible que se pueda hacer dentro del framework pero no fuera? – BenAlabaster

+0

Dónde está @JonSkeet cuando lo necesitas ... – BenAlabaster

Respuesta

15

Creo que es una mala interpretación de Reflector. En realidad, si usted escribe su código como este:

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

y compila en modo de lanzamiento verá 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 código C#, pero IL así lo ves en Reflector no siempre es la realidad. Así que para entender claramente lo que está pasando bajo el capó puede mirar el código real producida 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.

+0

@Darin, pero ¿no es eso semánticamente diferente? No es un nombre de usuario ahora una copia de x.ClassName y estamos pasando una referencia a la cadena "nombre de usuario" no una referencia a la cadena "x.ClassName" ... – BenAlabaster

+0

@Darin - ese extracto del código del modo de lanzamiento ahora me tiene rascándome la cabeza ... ¿cómo cambia el código a algo que no puedo codificar porque el compilador no lo permite? – BenAlabaster

+0

Lo mismo en el modo de depuración :-) –

1

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 debe apuntar en la dirección correcta. Como dijo Yuriy en su comentario anterior, es probable que tenga algo que ver con el hecho de que no está declarando explícitamente un obtener y establecer la propiedad.

+0

@Kyle - Ya había escrito esto como semánticamente diferente. Porque estaba buscando una referencia a x.ClassName. myClassName ahora sería una copia de la cadena y estaría pasando una referencia a la copia. Pero ahora @Darin Dimitrov me tiene preguntando acerca de la compilación del modo de lanzamiento ... – BenAlabaster

6

Es un error reflector. En realidad, no está pasando la propiedad por referencia.

Aquí hay algunos códigos C# que lo reproducirán.

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

Esto no sólo es válido C#, pero es engañosa - que sugiere que los cambios realizados en x dentro Bar de alguna manera modificar p.Name - donde eso no es el caso cuando miras el código C# original.

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

+0

Gracias por su entrada :) – BenAlabaster

+0

Mi muestra original está cortada y pegada directamente del reflector, que es lo que me hizo rascar la cabeza – BenAlabaster

+0

cuando digo "del reflector" Me refiero, por supuesto, a la interpretación de .NET Reflector de System.Web.dll (v2.0.50727) "Sé que eres un seguidor de terminología precisa: P – BenAlabaster

0

Cuando pasa una variable como ref o out, la llamada realmente apunta a la memoria donde se encuentra originalmente. Entonces, si le permiten 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.

Cuestiones relacionadas