Por que não posso passar uma propriedade ou indexador como um parâmetro ref quando .NET refletor mostra que ele é feito no .NET Framework?
-
05-07-2019 - |
Pergunta
Ok, eu vou cortar e colar a partir .NET refletor para demonstrar o que estou tentando fazer:
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 linha de código sai direito de System.Web.Security.SqlMembershipProvider.UpdateUser (System.Web.dll v2.0.50727) no .NET Framework.
O SecUtility.CheckParameter requer um valor de referência como o primeiro parâmetro, a que está passando uma propriedade do usuário passado como argumento.
A definição do código CheckParameter é:
internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
{
//Code omitted for brevity
}
Tudo que está fazendo faz sentido - no papel ... então eu bato um pouco protótipo rápida para um lugar que eu gostaria de usar algo semelhante:
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 não irá compilar - os shows de erro como:
"A property or indexer may not be passed as an out or ref parameter"
Fair suficiente ... mas por que não o meu código, permita-me fazer algo que parece estar na base de código .NET Framework? É este um erro com o caminho NET Reflector é interpretar a DLL ou isso é um erro com a maneira que eu estou interpretando seu código?
Solução
Eu acho que é alguma má interpretação do refletor. Na verdade, se você escrever seu código como este:
static void Main(string[] args)
{
DummyClass x = new DummyClass();
string username = x.ClassName;
DoSomething(ref username);
}
e compilá-lo no modo de versão você vai ver isso no refletor:
static void Main(string[] args)
{
DummyClass x = new DummyClass();
DoSomething(ref x.ClassName);
}
Lembre-se que o compilador C # não está produzindo código C #, mas IL modo que você vê no refletor nem sempre é a realidade. Então, para entender claramente o que está acontecendo sob o capô que você pode olhar para o verdadeiro código produzido pelo 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)
É claro que uma variável local é usado.
Outras dicas
É um bug refletor. Não é realmente passando a propriedade por referência.
Aqui está algum código C # que irá reproduzi-lo.
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)
{
}
}
Refletor mostra este código para Foo
:
private static void Foo(Person p)
{
Bar(ref p.Name);
}
Não só isto é inválida C #, mas é enganosa - isso sugeriria que as alterações feitas x
dentro Bar
de alguma forma modificar p.Name
-. Em que esse não é o caso quando você olha para o código original C #
Em sua amostra original, que faz ainda menos sentido como UserName
é uma propriedade somente leitura!
Tente definir o valor da propriedade a uma variável antes de passá-lo para a função.
string myClassName = x.ClassName
DoSomething(ref myClassName);
A sua não é a solução mais elegante, mas deve apontar na direção certa. Como Yuriy disse em seu comentário acima, seu provavelmente algo a ver com o fato de que você não está declarando explicitamente um get e set para a propriedade.
Quando você passar uma variável como um ref ou fora, a chamada realmente aponta para a memória onde é originalmente localizada. Então, se você tem permissão para passar a propriedade como uma referência, isso significa que você está fazendo o inconsistente membro da classe. Esta é a razão por trás deste problema.