Почему я не могу передать свойство или индексатор в качестве параметра ref, когда отражатель .NET показывает, что это сделано в .NET Framework?
Хорошо, я вырезаю и вставляю из отражателя .NET, чтобы продемонстрировать, что я пытаюсь сделать:
public override void UpdateUser(MembershipUser user)
{
//A bunch of irrelevant code...
SecUtility.CheckParameter(ref user.UserName, true, true, true, 0x100, "UserName");
//More irrelevant code...
}
Эта строка кода выводится из System.Web.Security.SqlMembershipProvider.UpdateUser(System.Web.dll v2.0.50727) в .NET Framework.
Для параметра SecUtility.CheckParameter требуется ссылочное значение в качестве первого параметра, которому передается свойство пользователя, переданного в качестве аргумента.
Определение кода CheckParameter:
internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
{
//Code omitted for brevity
}
Все, что он делает, имеет смысл - на бумаге... поэтому я нокаутирую маленький маленький прототип, где-то я хотел бы использовать что-то подобное:
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.
}
}
Этот код не будет компилироваться - ошибка отображается как:
"A property or indexer may not be passed as an out or ref parameter"
Достаточно честный... но почему мой код не позволит мне делать что-то, что похоже на базу кода .NET Framework? Это ошибка с тем, как .NET Reflector интерпретирует DLL или это ошибка с тем, как я интерпретирую их код?
Ответы
Ответ 1
Я думаю, что это плохая интерпретация из Reflector. Собственно, если вы пишете свой код следующим образом:
static void Main(string[] args)
{
DummyClass x = new DummyClass();
string username = x.ClassName;
DoSomething(ref username);
}
и скомпилируйте его в режиме Release, вы увидите это в Reflector:
static void Main(string[] args)
{
DummyClass x = new DummyClass();
DoSomething(ref x.ClassName);
}
Помните, что компилятор С# не создает код С#, но IL, поэтому то, что вы видите в Reflector, не всегда является реальностью. Чтобы четко понять, что происходит под капотом, вы можете посмотреть реальный код, созданный компилятором:
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)
Ясно, что используется локальная переменная.
Ответ 2
Это ошибка рефлектора. Это не передача свойства по ссылке.
Вот код С#, который будет воспроизводить его.
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)
{
}
}
Отражатель показывает этот код для Foo
:
private static void Foo(Person p)
{
Bar(ref p.Name);
}
Не только этот недопустимый С#, но он вводит в заблуждение - он предположил бы, что изменения, сделанные в x
внутри Bar
, каким-то образом изменили бы p.Name
- там, где это не так, когда вы смотрите на исходный код С#.
В вашем исходном примере это имеет еще меньшее значение, поскольку UserName
- свойство только для чтения!
Ответ 3
Попробуйте установить значение свойства переменной перед передачей его функции.
string myClassName = x.ClassName
DoSomething(ref myClassName);
Это не самое элегантное решение, но оно должно указывать на вас в правильном направлении. Как сказал Юрий в своем комментарии выше, вероятно, это связано с тем, что вы явно не объявляете get и не устанавливаете свойство.
Ответ 4
Когда вы передаете переменную как ref или out, вызов на самом деле указывает на память, где он изначально находится. Поэтому, если вам разрешено передать свойство в качестве ссылки, это означает, что вы делаете член класса непоследовательным. Это и есть причина этой проблемы.