Если разобрать его без сохранения оригинала; исходный пропал ; может храниться оригинал? т.е. 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# вид)