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

struct Foo {
    int i;
    public ref int I => ref i;
}

Этот код вызывает ошибку компиляции CS8170, но если Foo является классом, это не так. Почему структура не может вернуть элемент в качестве ссылки?


c#
person 20chan    schedule 09.03.2018    source источник
comment
На удивление сложно найти документацию по CS8170. Обычно у MSDN есть что-то, даже практически пустая страница-заполнитель, но в этом случае... Я нахожу некоторые проблемы с Github, модульные тесты Roslyn/исходный код, и все.   -  person    schedule 09.03.2018
comment
Поскольку типы значений (структуры) размещаются в стеке, ссылка, возвращаемая для одного из его членов, становится недействительной, как только переменная выходит за пределы области видимости, поэтому эта ошибка имеет смысл для безопасности кода.   -  person Bradley Smith    schedule 09.03.2018
comment
@BradleySmith - пожалуйста, перестаньте повторять это утомительное и не совсем верное утверждение. структуры иногда размещаются в стеке. множество случаев, когда это не так.   -  person Damien_The_Unbeliever    schedule 09.03.2018
comment
@BradleySmith Однако они не всегда размещаются в стеке.   -  person Ivan Stoev    schedule 09.03.2018
comment
Но неужели самого факта их размещения в стеке достаточно, чтобы объяснить ошибку компилятора?   -  person Bradley Smith    schedule 09.03.2018
comment
@BradleySmith, он прав. В вашем первоначальном комментарии говорится, что они выделены в стеке, а не в том, что они могут быть.   -  person    schedule 09.03.2018
comment
Возможно, если бы Microsoft могла обновить свою документацию, мы бы не увидели, что структуры размещаются в операторах стека.   -  person FCin    schedule 09.03.2018
comment
MS описывает это следующим образом: Однако, в отличие от классов, структуры являются типами значений и не требуют выделения кучи. См.: docs.microsoft.com/en-us/dotnet/csharp/language-reference/   -  person Stefan    schedule 09.03.2018
comment
Я бы хотел, чтобы у меня был попкорн, однако я думаю, что это достаточно справедливое объяснение того, что он будет недействительным, когда он выйдет за рамки (в некоторых обстоятельствах) и, следовательно, ошибка компилятора. Где скрывается Эрик Липперт   -  person TheGeneral    schedule 09.03.2018
comment
Забавно: это также относится к this, но согласно спецификациям, this будет означать копию структуры, что делает аргумент lives on stack (для this) недействительным. См.: docs.microsoft. com/en-us/dotnet/csharp/language-reference/   -  person Stefan    schedule 09.03.2018
comment
у нас должна быть возможность отправить этот вопрос @EricLippert. Я думаю, это был бы хороший адрес для этого.   -  person Mong Zhu    schedule 09.03.2018
comment
stackoverflow.com/questions/12801462/   -  person Essigwurst    schedule 09.03.2018
comment
@MongZhu вызывает Эрика Липперт действительно работает, хотя проще использовать ссылку «Контакты» в его блоге.   -  person    schedule 09.03.2018
comment
@Эми невероятно! ты на самом деле сделал это! Давай попробуем еще. Я очень впечатлен :)   -  person Mong Zhu    schedule 09.03.2018
comment
Какую IDE/компилятор вы используете? потому что, когда я пытаюсь скомпилировать ваш код, у меня много ошибок компиляции, но ни одна из них не называется CS8170   -  person Oxald    schedule 09.03.2018
comment
@Oxald Я использую VS2017, а версия компилятора 7.0. Вы проверили версию своего компилятора ›= 7.0?   -  person 20chan    schedule 09.03.2018
comment
Чтобы по-настоящему разобраться с ограничениями возврата ссылок, нужно понимать указатели. Указатели опасны, стандартная ошибка программирования в языке, который дает им неограниченные возможности, заключается в использовании указателя, который больше не обращается к допустимой ячейке памяти. C# не является таким языком, компилятор применяет синтаксис, который гарантирует, что такая ошибка никогда не появится. Дополнительным требованием является то, что он должен убедиться, что сборщик мусора может правильно обнаружить, на что указывает указатель, большая проблема в этом фрагмент. en.wikipedia.org/wiki/Dangling_pointer   -  person Hans Passant    schedule 09.03.2018
comment
Здесь есть некоторые дополнительные примечания: github.com/dotnet/csharplang/blob/master/meetings/2015/   -  person Pete Garafano    schedule 09.03.2018


Ответы (1)


Я думаю, что нашел способ обойти это:

class Program
{
    static void Main(string[] args)
    {
        Foo temp = new Foo(99);
        Console.WriteLine($"{Marshal.ReadInt32(temp.I)}");
        Console.ReadLine();
    }
}
struct Foo
{
    int i;
    public IntPtr I;

    public Foo(int newInt)
    {
        i = newInt;
        I = GetByRef(i);
    }
    static unsafe private IntPtr GetByRef(int myI)
    {
        TypedReference tr = __makeref(myI);
        int* temp = &myI;
        IntPtr ptr = (IntPtr)temp;
        return ptr;
    }
}

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

person daniel_sweetser    schedule 30.03.2018