Как сослаться на деконструированный кортеж значений, не делая его копии - PullRequest
6 голосов
/ 16 июня 2020

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

var (a, b, c) = GetValueTuple();

Однако теперь мне нужно передать весь кортеж другому методу, который его ожидает:

private void ExpectsTuple((a, b, c) tuple) { ... }

this.ExpectsTuple(/* need to pass tuple here */);

Есть ли простой или понятный способ «реконструировать» или передать всю структуру кортежа без необходимости создавать новую? Я пытался назвать кортеж, но это вызывает ошибку компиляции:

var (a, b, c) tuple = GetValueTuple();

Я знаю, что могу просто сделать ExpectsTuple((a, b, c)), но мне интересно, будет ли это без необходимости создавать копию моей структуры и есть ли более чистый способ сделать это.

1 Ответ

8 голосов
/ 16 июня 2020

Если разобрать его без сохранения оригинала; исходный пропал ; может храниться оригинал? т.е. var tuple = GetValueTuple()? Затем вы можете либо получить доступ к tuple.Foo et c, либо просто разобрать его потом :

Итак:

var tuple = GetValueTuple();
var (a, b, c) = tuple;
// not shown: something involving a, b, c
// ...
ExpectsTuple(tuple);

или

var tuple = GetValueTuple();
// not shown: something involving tuple.Foo, tuple.Bar, etc
// ...
ExpectsTuple(tuple);

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

void ExpectsTuple(in (...the tuple type...) tuple)
{...}
// ...
ExpectsTuple(tuple); // the "in" here is implicit

Что касается создания копии структуры: она будет делать это в любом случае - из-за семантика стека. Вопрос только в том, насколько эффективно он это может сделать (должен ли он выполнять конструктор? Или это просто блит?). Обратите внимание, что вы не можете использовать in, чтобы избежать этой дополнительной копии, потому что кортежи значений не readonly struct, поэтому использование in просто скопирует кортеж в стек, а затем передаст адрес копии; с таким же успехом вы можете просто передать копию в стек. Технически вы можете использовать ref, чтобы избежать копирования, но ... это неудобно и, вероятно, того не стоит в большинстве случаев. Это также оставляет ваш код во власти того, что ExpectsTuple(ref tuple) решит сделать с данными.

Лично я бы использовал подход tuple.Foo et c; поскольку кортежи обеспечивают прямой доступ к полям, это на самом деле более прямое, чем может показаться, особенно после того, как JIT выполнила то, что ему нужно.


Из комментариев:

Не могли бы вы подробнее рассказать о том, «насколько эффективно он может это сделать (должен ли он выполнять конструктор? Или это просто блит?)»

Что я имею в виду: когда вы просто передаете предварительный -существующее значение, все, что ему нужно сделать, это загрузить значение из стека - это просто быстрая копия памяти; однако, если вы его деконструировали, ему необходимо реконструировать , что означает: запуск конструктора ValueTuple<,,> - с любыми имеющимися logi c; вы можете увидеть, что здесь , посмотрев разницу между PassDirect и DeconstructAndReconstruct с правой стороны (что является интерпретацией кода после компилятор с этим покончил). Вы можете видеть, что ExpectsTuple((a, b, c)) заканчивается запуском ExpectsTuple(new ValueTuple<int, DateTime, string>(item, item2, item3)); код для этого конструктора несложен, но это лот сложнее, чем просто «загрузить значение, о котором я уже знал».

Я также должен поясните: код в правой панели не совсем правильный - нет на самом деле другого локального адреса со значением; чтобы увидеть, что на самом деле происходит, вам нужно взглянуть на представление IL - фактические локальные переменные объявлены вверху; см .:

        .locals init (
            [0] valuetype [System.Private.CoreLib]System.ValueTuple`3<int32, valuetype [System.Private.CoreLib]System.DateTime, string> tuple
        )

и

        .locals init (
            [0] int32 a,
            [1] valuetype [System.Private.CoreLib]System.DateTime b,
            [2] string c
        )

Обратите внимание, что у второго не есть оба, т.е. это не

        .locals init (
            [0] valuetype [System.Private.CoreLib]System.ValueTuple`3<int32, valuetype [System.Private.CoreLib]System.DateTime, string> tuple
            [1] int32 a,
            [2] valuetype [System.Private.CoreLib]System.DateTime b,
            [3] string c
        )

(несмотря на то, что предлагает C# вид)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...