Perché non riesco a passare una proprietà o un indicizzatore come parametro ref quando .NET reflector mostra che è stato eseguito in .NET Framework?

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

  •  05-07-2019
  •  | 
  •  

Domanda

Va ??bene, taglierò e incollerò da .NET Reflector per dimostrare cosa sto cercando di fare:

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

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

    //More irrelevant code...
}

Questa riga di codice esce direttamente da System.Web.Security.SqlMembershipProvider.UpdateUser (System.Web.dll v2.0.50727) in .NET Framework.

SecUtility.CheckParameter richiede un valore di riferimento come primo parametro, al quale stanno passando una proprietà dell'utente passata come argomento.

La definizione del codice CheckParameter è:

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

Tutto ciò che sta facendo ha un senso - sulla carta ... quindi faccio un breve prototipo per un posto in cui mi piacerebbe usare qualcosa di simile:

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

Questo codice non verrà compilato: l'errore viene visualizzato come:

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

Abbastanza giusto ... ma perché il mio codice non mi permette di fare qualcosa che sembra essere nella base di codice di .NET Framework? È un errore nel modo in cui .NET Reflector sta interpretando la DLL o è un errore nel modo in cui sto interpretando il loro codice?

È stato utile?

Soluzione

Penso che sia una cattiva interpretazione di Reflector. In realtà se scrivi il tuo codice in questo modo:

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

e compilarlo in modalità Release lo vedrai in Reflector:

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

Ricorda che il compilatore C # non produce codice C # ma IL, quindi ciò che vedi in Reflector non è sempre la realtà. Quindi per capire chiaramente cosa sta succedendo sotto il cofano puoi guardare il vero codice prodotto dal compilatore:

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)

È chiaro che viene utilizzata una variabile locale.

Altri suggerimenti

È un bug del riflettore. In realtà non passa la proprietà per riferimento.

Ecco del codice C # che lo riprodurrà.

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 mostra questo codice per Foo :

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

Non solo questo C # non è valido, ma è fuorviante: suggerirebbe che le modifiche apportate a x in Bar in qualche modo modificherebbero p.Name - dove non è il caso quando guardi il codice C # originale.

Nel tuo esempio originale, ha ancora meno senso poiché UserName è una proprietà di sola lettura!

Prova a impostare il valore della proprietà su una variabile prima di passarlo alla funzione.

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

Non è la soluzione più elegante, ma dovrebbe indirizzarti nella giusta direzione. Come ha detto Yuriy nel suo commento sopra, probabilmente ha a che fare con il fatto che non stai dichiarando esplicitamente un get e impostato per la proprietà.

Quando si passa una variabile come ref o out, la chiamata in realtà punta alla memoria in cui si trova originariamente. Quindi, se ti è permesso passare la proprietà come riferimento, significa che stai rendendo incoerente il membro della classe. Questa è la ragione dietro questo problema.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top