Свойство С# и параметр ref, почему нет сахара?

Я просто столкнулся с этим сообщением об ошибке во время работы в С#

Свойство или индексатор не может передаваться как параметр out или ref

Я знал, что вызвало это, и быстро разработало локальную переменную правильного типа, вызвав функцию с ней как параметр out/ref, а затем вернув ее обратно в свойство:

RefFn(ref obj.prop);

превращается в

{
    var t = obj.prop;
    RefFn(ref t);
    obj.prop = t;
}

Очевидно, что это не сработает, если свойство не поддерживает get и устанавливает в текущем контексте.

Почему С# не делает это для меня?


Единственные случаи, когда я могу думать о том, где это может вызвать проблемы, следующие:

  • резьбонарезной
  • исключения

Для потоковой передачи это преобразование влияет на то, когда происходит запись (после вызова функции или в вызове функции), но я скорее подозреваю, что любой код, который рассчитывает на это, будет немного сочувствовать, когда он сломается.

Для исключений, беспокойство будет; что произойдет, если функция назначит одному из нескольких параметров ref, чем бросает? Любое тривиальное решение приведет к тому, что все или ни один из параметров не будет назначен, когда некоторые из них должны быть, а некоторые не должны быть. Снова я не думаю, что это будет поддерживать использование языка.


Примечание. Я понимаю механику того, почему генерируются эти сообщения об ошибках. То, что я ищу, является основанием для того, почему С# не выполняет автоматически тривиальное обходное решение.

Ответ 1

Просто для информации, С# 4.0 будет иметь что-то вроде этого сахара, но только при вызове методов взаимодействия - отчасти из-за явной склонности ref в этом сценарии. Я не тестировал это много (в CTP); мы должны будем увидеть, как он выталкивается...

Ответ 2

Потому что вы передаете результат индексатора, который действительно является результатом вызова метода. Там нет гарантии, что свойство indexer также имеет сеттер, и передача его с помощью ref приведет к ложной безопасности в части разработчика, когда он подумает, что его свойство будет установлено без вызова сеттера.

На более техническом уровне ref и out передают адрес памяти переданного им объекта, а для установки свойства вы должны вызвать setter, поэтому нет гарантии, что свойство действительно будет изменено, особенно когда тип свойства неизменен. ref и out не просто устанавливают значение при возврате метода, они передают фактическую ссылку на память самому объекту.

Ответ 3

Свойства - это не что иное, как синтаксический сахар по методам getX/setX в стиле Java. Это не имеет смысла для "ref" метода. В вашем случае это будет иметь смысл, потому что ваши свойства просто вычеркивают поля. Свойствам необязательно просто быть заглушки, поэтому фреймворк не может разрешать "ref" в свойствах.

EDIT. Ну, простой ответ заключается в том, что простой факт, что свойство getter или setter может включать гораздо больше, чем просто чтение/запись поля, делает нежелательным, не говоря уже о неожиданном, разрешите сорт сахара, который вы предлагаете. Это не значит, что я раньше не нуждался в этой функции, просто понимаю, почему они не хотят ее предоставлять.

Ответ 4

Вы можете использовать поля с ref/out, но не свойствами. Причина в том, что свойства - это просто синтаксис для специальных методов. Компилятор фактически переводит свойства get/set в соответствующие методы get_X и set_X, так как среда CLR не имеет непосредственной поддержки свойств.

Ответ 5

Это не было бы поточно-безопасным; если два потока одновременно создают свои собственные копии значения свойства и передают их в функции как параметры ref, только один из них возвращается в свойство.

class Program
{
  static int PropertyX { get; set; }

  static void Main()
  {
    PropertyX = 0;

    // Sugared from: 
    // WaitCallback w = (o) => WaitAndIncrement(500, ref PropertyX);
    WaitCallback w = (o) => {
      int x1 = PropertyX;
      WaitAndIncrement(500, ref x1);
      PropertyX = x1;
    };
    // end sugar

    ThreadPool.QueueUserWorkItem(w);

    // Sugared from: 
    // WaitAndIncrement(1000, ref PropertyX);
    int x2 = PropertyX;      
    WaitAndIncrement(1000, ref x2);
    PropertyX = x2;
    // end sugar

    Console.WriteLine(PropertyX);
  }

  static void WaitAndIncrement(int wait, ref int i)
  {
    Thread.Sleep(wait);
    i++;
  }
}

Свойство X заканчивается как 1, тогда как поле или локальная переменная будут равны 2.

В этом примере кода также показаны трудности, возникающие в таких вещах, как анонимные методы, когда просят компилятор делать сладкие вещи.

Ответ 6

Когда вы передаете добавление ref/out, это означает, что вы передаете ссылочный тип, который хранится в куче.

Свойства - это методы-обертки, а не переменные.

Ответ 7

Причиной этого является то, что С# не поддерживает "параметрические" свойства, которые принимают параметры, переданные по ссылке. Интересно отметить, что CLR поддерживает эту функцию, но С# не делает.

Ответ 8

Если вы спрашиваете, почему компилятор не подставляет поле, возвращаемое элементом getter, это значит, что getter может возвращать константу или readonly или literal или что-то еще, которые не следует переинициализировать или перезаписать.

Ответ 9

У этого сайта есть работа для вас. Я не тестировал его, поэтому я не могу гарантировать, что он будет работать. В примере, как представляется, используется отражение, чтобы получить доступ к функциям get и set свойства. Это, вероятно, не рекомендуемый подход, но он может выполнить то, о чем вы просите.

http://www.codeproject.com/KB/cs/Passing_Properties_byref.aspx