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


142

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

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

8
эм ... цитата пожалуйста?
RPM1984,

5
В C # есть значения и ссылочные типы, и оба могут быть возвращены, что вы имеете в виду.
повторный

Я говорю о чем-то вродеreturn ref x
Tom Sarduy

2
Через 4 года вы можете сделать именно это с C# 7:)
Arghya C

Ответы:


189

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

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

ОБНОВЛЕНИЕ: эта функция вошла в C # 7!


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

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

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

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!

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

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

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

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 в этой части программы. Это не очень большая работа разработчика, но это большая нагрузка на команды тестирования, чтобы убедиться, что у нас действительно есть все кейсы. Это просто еще одна вещь, которая увеличивает стоимость функции до такой степени, что прямо сейчас преимущества не перевешивают затраты.

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

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


4
@EricLippert: У меня нет убедительного примера, мне просто интересно. Превосходный и убедительный ответ
Tom Sarduy

1
@Eric: В твоем примере, не лучше ли было бы остаться в y живых после возвращения из M2? Я ожидал, что эта функция будет работать как лямбды, захватывающие локальных жителей. Или поведение, которое вы предложили, связано с тем, как CLR обрабатывает этот сценарий?
Fede

3
@Eric: ИМХО, возможность иметь свойства, возвращающие ссылки на типы значений, является серьезным упущением в языках .net. Если Arr является массивом типа значения (например, Point), можно сказать, например, Arr (3) .X = 9, и знать, что никто не изменил значение Arr (9) .X или даже SomeOtherArray (2). ИКС; если бы Arr был массивом некоторого ссылочного типа, таких гарантий не существовало бы. Тот факт, что оператор индексирования массива возвращает ссылку, чрезвычайно полезен; Я считаю очень прискорбным, что никакой другой тип коллекции не может обеспечить такую ​​функциональность.
supercat

4
Эрик, как лучше всего дать обратную связь относительно сценариев (как вы предлагаете в последнем абзаце), чтобы максимизировать шансы на то, что они увидят? MS Connect? UserVoice (с произвольным ограничением 10 постов / голосов)? Что-то другое?
Роман Старков

1
@ThunderGr: Это философия C # для «небезопасного» - если вы пишете код, который, возможно, небезопасен для памяти, то C # настаивает на том, чтобы вы пометили его как «небезопасный», чтобы вы взяли на себя ответственность за безопасность памяти. В C # уже есть небезопасная версия функции при условии, что рассматриваемая переменная имеет неуправляемый тип. Вопрос в том, должна ли команда C # делать небезопасную версию, которая обрабатывает управляемые типы. Если это позволяет разработчику легко писать ужасные ошибки, этого не произойдет. C # - это не C ++, язык, на котором легко писать ужасные ошибки. C # безопасен по своей конструкции.
Эрик Липперт

21

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

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] массива возвращает ссылку на структуру. Невозможно написать собственный индексатор (например, для настраиваемой коллекции), который имеет такое же поведение «возврат ссылки».

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


8
Том спрашивает о методах, возвращающих ссылку на переменную . Переменная не обязательно должна иметь тип значения, хотя, конечно, обычно люди хотят этого, когда им нужны методы, возвращающие ссылку. В остальном отличный анализ; вы правы, что единственное место на языке C #, где сложное выражение создает ссылку на переменную, которой пользователь может затем манипулировать, - это индексатор массива. (И, конечно же, оператор доступа к члену «.» Между получателем и полем, но это, очевидно, доступ к переменной.)
Эрик Липперт,

1

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.