Почему я не могу передать свойство или индексатор в качестве параметра ref, когда .NET рефлектор показывает, что это сделано в .NET Framework?

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top