Возможно ли вернуть ссылку на переменную в С#?

Можно ли вернуть ссылку на двойное значение?

Это то, что я хочу сделать:

ref double GetElement()
{
   ......
   // Calculate x,y,z
   return ref doubleArray[x,y,z];
}

Чтобы использовать его так:

void func()
{
   GetElement()=5.0;
}

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

Ответ 1

Типы значений в С# всегда передаются по значению. Объекты всегда имеют ссылку, переданную по значению. Это изменяется в "небезопасном" коде, как указывает Axarydax.

Самый простой и безопасный способ избежать этого ограничения - убедиться, что ваш двойник каким-то образом привязан к объекту.

public class MyObjectWithADouble {
    public double Element {get; set;} // property is optional, but preferred.
}

...
var obj = new MyObjectWithADouble();
obj.Element = 5.0

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

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

public class 3dArrayLocation {public int X; public int Y; public int Z;}

...

public 3dArrayLocation GetElementLocation(...)
{
    // calculate x, y, and z
    return new 3dArrayLocation {X = x, Y = y, Z = z}
}

...

var location = GetElementLocation(...);
doubleArray[location.X, location.Y, location.Z] = 5.0;

Ответ 2

UPDATE: желаемая функция теперь поддерживается на С# 7.


Система типа CLR поддерживает методы ref-return, и я написал экспериментальный прототип компилятора С#, который поддерживает функцию, которую вы хотите. (Прототип также реализует локализованные локальные переменные, но заполненные поля являются незаконными в системе типа CLR.)

Вы выбрали именно тот синтаксис, который я выбрал для прототипа, что означает, что либо великие мысли думают одинаково, либо что дураки никогда не отличаются.

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

Например, вы можете сделать пару делегатов:

struct Ref<T>
{
    private readonly Func<T> getter;
    private readonly Action<T> setter;
    public Ref(Func<T> getter, Action<T> setter)
    {
        this.getter = getter;
        this.setter = setter;
    }
    public T Value { get { return getter(); } set { setter(value); } }
}

var arr = new int[10];
var myref = new Ref<int>(()=>arr[1], x=>arr[1]=x);
myref.Value = 10;
Console.WriteLine(myref.Value);

Это значительно медленнее, чем та же функция, реализованная с возвратом возврата, но преимущество в том, что вы можете сделать Ref<T> в местах, где ref не является законным. Например, вы можете хранить Ref<T> в поле, которое вы не можете сделать с помощью метода ref-ref.

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

См. также связанные вопросы:

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

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

и мой пост в блоге по теме:

http://ericlippert.com/2011/06/23/ref-returns-and-ref-locals/

Ответ 3

Нет, это невозможно в С#. Вы можете передавать только параметры по ссылке.

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

double Element
{
    get { return doubleArray[x,y,z]; }
    set { doubleArray[x,y,z] = value; }
}

void func()
{
   Element = 5.0;
}

Ответ 4

Вы можете сделать это в небезопасном коде и иметь метод, который возвращает указатель на double:

unsafe double* GetElementP(){
...
}

Ответ 5

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

void SetElement(double value)
{
   ......
   // Calculate x,y,z
   doubleArray[x,y,z]=value;
}

и используйте его:

void func()
{
   SetElement(5.0);
}

Однако я все равно выберу один из ваших полезных ответов...