Свойство C # и параметр ref, почему нет сахара? - PullRequest
70 голосов
/ 09 февраля 2009

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

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

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

RefFn(ref obj.prop);

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

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

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

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


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

  • потоковая
  • Исключения

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

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


Примечание: я понимаю механизм, почему генерируется это сообщение об ошибке. Что я ищу, так это обоснование того, почему C # не выполняет автоматически тривиальный обходной путь.

Ответы [ 9 ]

32 голосов
/ 09 февраля 2009

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

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

17 голосов
/ 09 февраля 2009

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

РЕДАКТИРОВАТЬ : Ответ прост: тот факт, что метод получения или установки свойства может включать в себя гораздо больше, чем просто поле чтения / записи, делает нежелательным, не говоря уже о неожиданном, сорт сахара, который вы предлагаете. Это не значит, что я раньше не нуждался в этой функции, просто я понимаю, почему они не захотят ее предоставлять.

11 голосов
/ 09 февраля 2009

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

9 голосов
/ 09 февраля 2009

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

6 голосов
/ 10 февраля 2009

Это не будет потокобезопасным; если два потока одновременно создают свои собственные копии значения свойства и передают их функциям в качестве параметров 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++;
  }
}

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

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

4 голосов
/ 09 февраля 2009

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

4 голосов
/ 09 февраля 2009

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

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

1 голос
/ 09 февраля 2009

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

0 голосов
/ 09 февраля 2009

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

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

...