Если C# Action
, ссылающийся на метод класса, по-прежнему имеет ссылку на исходный Target
, то почему this
изменяется, когда я вызываю Action
и устанавливаю точки останова в этом методе?
У меня есть следующая настройка в проекте .NET:
// MyClass.cs
public class MyClass
{
private float _someValue = -1f;
private int _instanceId = 12345;
public void Method()
{
Updater.Instance.RegisterUpdate(_instanceId, doStuff);
}
private void doStuff(float deltaTime)
{
// Do stuff with _someValue
}
}
// Updater.cs
public class Updater
{
public static Updater Instance = new();
private readonly List<Action<float>> _updateActions = new();
public void RegisterUpdate(int instanceId, Action<float> updateAction)
{
// Validation...
_updateActions.Add(updateAction);
}
public void Update()
{
foreach (Action<float> action in _updateActions)
action.Invoke(Time.deltaTime);
}
}
По сути, Updater
оборачивает список зарегистрированных Action
и вызывает их все при вызове Update
.
В Visual Studio я могу установить точки останова в MyClass.doStuff()
, и они срабатывают при вызове Updater.Update()
. К сожалению, во время паузы в точке останова я не получаю никакого значения при наведении курсора на значение _someValue
, и это поле даже не упоминается в окне «Локальные», а когда я ввожу _someValue
в окне Immediate, я получаю «Идентификатор _someValue
не в размахе». Кроме того, значение this
в окне Locals имеет тип Updater
, а не MyClass
.
И тем не менее, все работает нормально. Условная логика внутри doStuff
, которая работает с _someValue
, по-прежнему работает, как и ожидалось, когда я выполняю шаг, я просто не могу проверять значения во время шага, что затрудняет отладку. Кроме того, когда я поднимаюсь по стеку вызовов к Updater.Update
и проверяю текущий элемент _updateActions
, я вижу, что его свойство Target
правильно установлено на экземпляр MyClass
. Так что же здесь происходит? Попытка понять, что такое this
, кажется дерьмом JavaScript, а не C#.
@knittl Я тоже этого ожидал. Но я говорю, что, находясь внутри doStuff
, this
на самом деле относится к Updater
Пожалуйста, предоставьте полный минимально воспроизводимый пример. То, что вы описываете, не может быть.
@NineBerry Я пытался указать MWE выше в своем вопросе; единственное, что я не учел, это код для вызова Updater.Update
, но это может быть где угодно. Технически были некоторые синтаксические ошибки, но я их просто отредактировал. Возможно, стоит упомянуть, что этот код выполняется в скриптах Unity, поэтому в моем проекте метод Updater.Update
на самом деле вызывается движком Unity, а не каким-либо моим кодом.
Предоставленный код не компилируется, потому что все еще есть синтаксические ошибки, которые предполагают, что вы не скопировали его из тестового проекта, а придумали на ходу. Следуйте инструкциям по созданию минимального воспроизводимого примера.
@NineBerry Вы правы, я придумал это на ходу; фактический код немного сложнее и зависит от движка Unity, поэтому вместо того, чтобы сокращать кучу кода, я просто создал этот MWE. Основная логика осталась прежней. Я только что отредактировал свой ответ, чтобы исправить оставшиеся ошибки компилятора. Единственное, чего сейчас не хватает, так это кода для вызова Updater.Update
, который опять же может быть чем угодно (модульный тест, статический Main
метод, сообщение Unity Update
или что-то еще).
Вы воспроизвели проблему, используя точный код из вопроса?
Опубликованный код не воспроизводит проблему imgur.com/a/FM4qub2
@NineBerry При дальнейшем расследовании похоже, что проблема в том, что в моем реальном коде метод doStuff
является локальным методом, определенным внутри Method
. Когда я делаю doStuff
полный метод (как в моем фрагменте выше), тогда this
имеет правильный MyClass
тип, как вы заметили. Кроме того, я могу воспроизвести это поведение только в компонентах Unity, а не в ванильных классах C#, так что это может быть результатом компилятора Mono, который Unity использует на сервере. Спасибо, что побудили меня копнуть глубже.
FWIW, учитывая мою оценку репутации на этом сайте, я думаю, можно предположить, что я понимаю, что такое минимальный рабочий пример, и пытался его предоставить. Очевидно, в этом случае я плохо справился, но я быстро отредактировал свой ответ на основе приведенных выше комментариев и теперь занялся реальной проблемой. Продолжать отрицать мой вопрос без объяснения причин крайне грубо и обескураживающе.
Оказывается, проблема заключалась в том, что в моем реальном коде не было отражено в минимальном рабочем примере в моем вопросе. То есть в моем реальном коде doStuff
была локальной функцией внутри Method
. Если doStuff
— это отдельный метод, как в моем вопросе, то this
относится к правильному типу MyClass
. Как ни странно, я могу воспроизвести this
-изменяющееся поведение локальных функций только в компонентах Unity, а не в ванильных классах C#, так что это может быть результатом компилятора Mono, который Unity использует на бэкэнде.
В любом случае, спасибо комментаторам за то, что помогли мне попасть сюда. Надеюсь, это поможет кому-то в будущем.
Пока вы находитесь внутри функций класса,
this
указывает на окружающий класс функции. Когда вы вызываетеUpdater.update
, тоthis
имеет типUpdater
. Пока внутриdoStuff
,this
будетMyClass
.