Почему я не могу передать свойство или индексатор в качестве параметра ref, когда .NET рефлектор показывает, что это сделано в .NET Framework?
-
05-07-2019 - |
Вопрос
Хорошо, я буду вырезать и вставлять из рефлектора .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, или это ошибка в том, как я интерпретирую их код?
Решение
Я думаю, что это плохая интерпретация от 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);
}
Помните, что компилятор C # создает не код C #, а 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)
Понятно, что используется локальная переменная.
Другие советы
Это ошибка отражателя. На самом деле это свойство не передается по ссылке.
Вот код C #, который будет воспроизводить его.
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);
}
Это не только недопустимый C #, но он вводит в заблуждение - он предполагает, что изменения, внесенные в x
внутри Bar
, каким-то образом изменят p.Name
- где это не тот случай, когда вы смотрите на оригинальный код C #.
В исходном примере это имеет еще меньше смысла, поскольку UserName
- свойство только для чтения!
Попробуйте установить значение свойства в переменной, прежде чем передавать его в функцию.
string myClassName = x.ClassName
DoSomething(ref myClassName);
Это не самое элегантное решение, но оно должно указывать вам правильное направление. Как сказал Юрий в своем комментарии выше, вероятно, это связано с тем, что вы явно не объявляете get и set для свойства.
Когда вы передаете переменную как ref или out, вызов фактически указывает на память, в которой она изначально находилась. Поэтому, если вам разрешено передавать свойство в качестве ссылки, это означает, что вы делаете элемент класса непоследовательным. Это причина этой проблемы. Р>