Подтвердить что ты не робот

Почему я не могу передать свойство или индексатор в качестве параметра 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 или это ошибка с тем, как я интерпретирую их код?

4b9b3361

Ответ 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, вызов на самом деле указывает на память, где он изначально находится. Поэтому, если вам разрешено передать свойство в качестве ссылки, это означает, что вы делаете член класса непоследовательным. Это и есть причина этой проблемы.