Почему С# не поддерживает возврат ссылок?

Я читал, что .NET поддерживает возврат ссылок, но С# этого не делает. Есть ли особая причина? Почему я не могу сделать что-то вроде:

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

Ответ 1

Этот вопрос был предметом моего блога 23 июня 2011 года. Спасибо за отличный вопрос!

Команда С# рассматривает это для С# 7. Подробнее см. https://github.com/dotnet/roslyn/issues/5233.

UPDATE: функция переместилась на С# 7!


Вы правы;.NET поддерживает методы, которые возвращают управляемые ссылки на переменные..NET также поддерживает локальные переменные, которые содержат управляемые ссылки на другие переменные. (Обратите внимание, что .NET не поддерживает поля или массивы, которые содержат управляемые ссылки на другие переменные, поскольку это слишком усложняет историю сбора мусора. Также "управляемая ссылка на переменную", типы не конвертируются в объект и поэтому не могут использоваться в качестве аргументов типа для общих типов или методов.)

Комментарий "RPM1984" почему-то попросил процитировать этот факт. RPM1984 Я рекомендую вам прочитать спецификацию CLI Раздел я Раздел 8.2.1.1 "Управляемые указатели и связанные типы" для получения информации об этой функции .NET.

Вполне возможно создать версию С#, которая поддерживает обе эти функции. Затем вы могли бы сделать что-то вроде

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

а затем вызовите его с помощью

int a = 123;
int b = 456; 
ref int c = ref Max(ref a, ref b); 
c += 100;
Console.WriteLine(b); // 556!

Я эмпирически знаю, что можно создать версию С#, которая поддерживает эти функции, потому что я это сделал. Продвинутые программисты, особенно люди, переносящие неуправляемый код на С++, часто спрашивают нас о большей способности С++ делать что-то со ссылками, не выходя из большого молотка, фактически используя указатели и фиксируя память повсюду. Используя управляемые ссылки, вы получаете эти преимущества, не оплачивая затраты на сборку производительности мусора.

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

Кроме того, для правильного выполнения этого потребуется внести некоторые изменения в CLR. В настоящее время CLR рассматривает методы ref-return как законные, но непроверяемые, потому что у нас нет детектора, который обнаруживает эту ситуацию:

ref int M1(ref int x)
{
    return ref x;
}

ref int M2()
{
    int y = 123;
    return ref M1(ref y); // Trouble!
}

int M3()
{
    ref int z = ref M2();
    return z;
}

M3 возвращает содержимое локальной переменной M2, но время жизни этой переменной завершено! Можно написать детектор, который определяет использование ref-возвратов, которые явно не нарушают безопасность стека. То, что мы будем делать, это написать такой детектор, и если детектор не сможет доказать безопасность стека, мы не допустим использования возвратов ref в этой части программы. Это не огромная работа разработчиков, но для групп тестирования очень много нагрузки, чтобы убедиться, что у нас действительно есть все случаи. Это еще одна вещь, которая увеличивает стоимость этой функции до такой степени, что сейчас выгоды не перевешивают затраты.

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

(См. также связанные вопросы Возможно ли вернуть ссылку на переменную в С#? и Могу ли я использовать ссылка внутри функции С#, например С++?)

Ответ 2

Вы говорите о методах, возвращающих ссылку на тип значения. Единственный встроенный пример в С#, о котором я знаю, - это тип доступа к массиву типа значения:

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

и теперь создайте массив этой структуры:

var points = new Point[10];
points[0].X = 1;
points[0].Y = 2;

В этом случае points[0] массив indexer возвращает ссылку на struct. Невозможно написать свой собственный индекс (например, для пользовательской коллекции), который имеет такое же поведение "вернуть ссылку".

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

Ответ 3

Вы всегда можете сделать что-то вроде:

public delegate void MyByRefConsumer<T>(ref T val);

public void DoSomethingWithValueType(MyByRefConsumer<int> c)
{
        int x = 2;
        c(ref x);
        //Handle potentially changed x...
}

Ответ 4

С# 7.0 поддерживает возврат ссылок. См. Мой ответ здесь.