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

Доступ к члену в форме может вызвать исключение во время выполнения, поскольку он является полем класса маршала по ссылке

Доступ к члену в форме может вызвать исключение во время выполнения, поскольку он является полем класса маршала по ссылке

Я знаю, что это за предупреждение, и знаю, как его решить.

Мой вопрос: почему это может привести к ошибке выполнения?

4b9b3361

Ответ 1

Вероятно, вы говорите о предупреждении CS1690, воспроизводите код:

public class Remotable : MarshalByRefObject {
    public int field;
}
public class Test {
    public static void Run() {
        var obj = new Remotable();
        // Warning CS1690:
        Console.WriteLine(obj.field.ToString());
    }
}

В удаленном сценарии метод Test.Run будет работать с прокси-сервером объекта Remotable. Построение прокси-сервера для свойства, метода или события не является большой проблемой, а просто создаёт MethodTable, который содержит заменители. Поля - проблема, однако ничего не стоит "зацепить". Для MBRO компилятор JIT больше не генерирует код для прямого доступа к полю, он в этом случае вызывает вызов вспомогательного метода, встроенного в CLR, JIT_GetField32().

Этот помощник проверяет, является ли объект прокси-сервером, и использует удаленную сантехнику для получения удаленного значения, если это случай. Или просто обращается к полю напрямую, если это не так. Однако для вызова ToString() требуется ввести значение. В этой проблеме бокс изолирует значение от прокси. Невозможно гарантировать, что значение в штучной упаковке всегда является точной копией удаленного значения. Вызов JIT_GetField32() снова, когда метод ToString() использует значение для форматирования строки, невозможно.

Обходной путь для CS1690 прост, за исключением поля с свойством, просто скопируйте значение поля в локальную переменную. Теперь совершенно ясно, что код работает с копией, и никогда не бывает сюрпризов, поэтому компилятору не придется выдавать предупреждение.

public static void Run() {
    var obj = new Remotable();
    var value = obj.field;
    Console.WriteLine(value.ToString());     // No warning
}

Ответ 2

В дополнение к предложению @hans-passant, я думаю, что еще один полезный способ исправить это предупреждение - это превратить ваше поле в свойство.

public class Remotable : MarshalByRefObject {
    public int field;
}

может стать

public class Remotable : MarshalByRefObject {
    public int field { get; set }
}

и вы больше не получаете никаких предупреждений! (Ханс Пассант уже имеет отличное объяснение этому, см. его сообщение)

Очевидно, что вы не всегда можете изменить объект, с которым работаете (например: WinForms, где поля генерируются для вас), поэтому вам может потребоваться отменить использование временной переменной.

Ответ 3

Если другая сторона объекта маршаллинга умерла, он выкинет ошибку времени выполнения, указав, что ссылочный объект больше не существует.

Ответ 4

Или вы можете написать:

var obj = new Remotable();

Console.WriteLine(((int) obj.field).ToString());     // No warning

Здесь вы берете на себя ответственность за этот прилив (распаковка).