mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
655 lines
18 KiB
655 lines
18 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Text; |
|
using System.Threading.Tasks; |
|
|
|
namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness |
|
{ |
|
static class KeyValuePairExtensions |
|
{ |
|
public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> pair, out TKey key, out TValue value) |
|
{ |
|
key = pair.Key; |
|
value = pair.Value; |
|
} |
|
} |
|
|
|
static class TupleClassExtensions |
|
{ |
|
public static void Deconstruct<T1, T2>(this Tuple<T1, T2> tuple, out T1 item1, out T2 item2) |
|
{ |
|
item1 = tuple.Item1; |
|
item2 = tuple.Item2; |
|
} |
|
} |
|
|
|
class DeconstructionTests |
|
{ |
|
public static void Main() |
|
{ |
|
new DeconstructionTests().Test(); |
|
} |
|
|
|
public struct MyInt |
|
{ |
|
public static implicit operator int(MyInt x) |
|
{ |
|
Console.WriteLine("int op_Implicit(MyInt)"); |
|
return 0; |
|
} |
|
|
|
public static implicit operator MyInt(int x) |
|
{ |
|
Console.WriteLine("MyInt op_Implicit(int)"); |
|
return default(MyInt); |
|
} |
|
} |
|
|
|
private class DeconstructionSource<T, T2> |
|
{ |
|
public int Dummy { |
|
get; |
|
set; |
|
} |
|
|
|
public void Deconstruct(out T a, out T2 b) |
|
{ |
|
Console.WriteLine("Deconstruct"); |
|
a = default(T); |
|
b = default(T2); |
|
} |
|
} |
|
|
|
private class AssignmentTargets |
|
{ |
|
int id; |
|
|
|
public AssignmentTargets(int id) |
|
{ |
|
this.id = id; |
|
} |
|
|
|
public int IntField; |
|
|
|
public int? NullableIntField; |
|
|
|
public MyInt MyIntField; |
|
|
|
public MyInt? NullableMyIntField; |
|
|
|
public MyInt My { |
|
get { |
|
Console.WriteLine($"{id}.get_My()"); |
|
return default(MyInt); |
|
} |
|
set { |
|
Console.WriteLine($"{id}.set_My({value})"); |
|
} |
|
} |
|
|
|
public MyInt? NMy { |
|
get { |
|
Console.WriteLine($"{id}.get_NMy()"); |
|
return default(MyInt?); |
|
} |
|
set { |
|
Console.WriteLine($"{id}.set_NMy({value})"); |
|
} |
|
} |
|
|
|
public int IntProperty { |
|
get { |
|
Console.WriteLine($"{id}.get_IntProperty()"); |
|
return default(int); |
|
} |
|
set { |
|
Console.WriteLine($"{id}.set_IntProperty({value})"); |
|
} |
|
} |
|
|
|
public uint UIntProperty { |
|
get { |
|
Console.WriteLine($"{id}.get_UIntProperty()"); |
|
return default(uint); |
|
} |
|
set { |
|
Console.WriteLine($"{id}.set_UIntProperty({value})"); |
|
} |
|
} |
|
} |
|
|
|
private DeconstructionSource<T, T2> GetSource<T, T2>() |
|
{ |
|
Console.WriteLine("GetSource()"); |
|
return new DeconstructionSource<T, T2>(); |
|
} |
|
|
|
private (T, T2) GetTuple<T, T2>() |
|
{ |
|
Console.WriteLine("GetTuple<T, T2>()"); |
|
return default(ValueTuple<T, T2>); |
|
} |
|
|
|
private (T, T2, T3) GetTuple<T, T2, T3>() |
|
{ |
|
Console.WriteLine("GetTuple<T, T2, T3>()"); |
|
return default(ValueTuple<T, T2, T3>); |
|
} |
|
|
|
private AssignmentTargets Get(int i) |
|
{ |
|
Console.WriteLine($"Get({i})"); |
|
return new AssignmentTargets(i); |
|
} |
|
|
|
public void Test() |
|
{ |
|
Property_NoDeconstruction_SwappedAssignments(); |
|
Property_NoDeconstruction_SwappedInits(); |
|
Property_IntToUIntConversion(); |
|
NoDeconstruction_NotUsingConver(); |
|
NoDeconstruction_NotUsingConver_Tuple(); |
|
NullReferenceException_Field_Deconstruction(out _); |
|
NullReferenceException_RefLocalReferencesField_Deconstruction(out _); |
|
NullReferenceException_RefLocalReferencesArrayElement_Deconstruction(out _, null); |
|
DeconstructTupleSameVar(("a", "b")); |
|
DeconstructTupleListForEachSameVar(new List<(string, string)> { ("a", "b") }); |
|
StructDeconstruction_Assignment(new NestedInner { Value = 7 }); |
|
NestedDeconstruction_Assignment(new NestedOuter { Value = 42 }); |
|
NestedDeconstruction_ForEach(new List<NestedOuter> { |
|
new NestedOuter { Value = 1 }, |
|
new NestedOuter { Value = 2 } |
|
}); |
|
NestedDeconstruction_DiscardedElement(new KeyValuePair<object, DiscardData>("key", default(DiscardData))); |
|
NestedDeconstruction_ClassInner(new ClassInnerOuter { Value = 7 }); |
|
NestedDeconstruction_Depth3(new DeepOuter { Value = 3 }); |
|
NestedDeconstruction_LhsSideEffects_DeconstructionOrder_Assignments(); |
|
NestedDeconstruction_Conversions_AfterAllDeconstructCalls(); |
|
NestedDeconstruction_TypedDeclaration_Conversions(new NestedOuter { Value = 5 }); |
|
NestedDeconstruction_DiscardWithSideEffectTargets(); |
|
NestedDeconstruction_HiddenDeconstructMethod(default(HidingOuter)); |
|
NestedDeconstruction_TupleWithCustomElement((7, new NestedInner { Value = 3 })); |
|
NestedDeconstruction_SystemTupleSource(Tuple.Create(8, new NestedInner { Value = 4 })); |
|
NestedDeconstruction_CheckedConversions(new NestedOuter { Value = 9 }); |
|
NestedDeconstruction_GenericConstraintSource(new ConstrainedSource { Value = 11 }); |
|
NestedDeconstruction_InParameterSource(new NestedOuter { Value = 12 }); |
|
NestedDeconstruction_ConditionalSource(c: true, new NestedOuter { Value = 13 }, new NestedOuter { Value = 14 }); |
|
NestedDeconstruction_TupleOuterConversions((15, new NestedInner { Value = 6 })); |
|
NestedDeconstruction_TypedConversions_UnrelatedCallAfter(new NestedOuter { Value = 16 }); |
|
NestedDeconstruction_NullableConversions(new NestedOuter { Value = 17 }); |
|
NestedDeconstruction_MyIntConversionOnNestedLeaves(new NestedOuter { Value = 18 }); |
|
NestedDeconstruction_ForEachDictionary_Conversions(new Dictionary<string, NestedInner> { |
|
{ "k1", new NestedInner { Value = 19 } } |
|
}); |
|
NestedTupleDeconstruction_Values((20, (21, 22))); |
|
NestedTupleDeconstruction_Depth3((23, (24, (25, 26)))); |
|
NestedTupleDeconstruction_Conversions((27, (28, 29))); |
|
NestedTupleDeconstruction_ElementUsedOutside((30, (31, 32))); |
|
NestedTupleDeconstruction_ForEach(new List<(int, (int, int))> { |
|
(33, (34, 35)), |
|
(36, (37, 38)) |
|
}); |
|
} |
|
|
|
public void NestedTupleDeconstruction_Values((int, (int, int)) t) |
|
{ |
|
Console.WriteLine("NestedTupleDeconstruction_Values:"); |
|
var (x, (a, b)) = t; |
|
Console.WriteLine(x + " " + a + " " + b); |
|
} |
|
|
|
public void NestedTupleDeconstruction_Depth3((int, (int, (int, int))) t) |
|
{ |
|
Console.WriteLine("NestedTupleDeconstruction_Depth3:"); |
|
var (x, (a, (b, c))) = t; |
|
Console.WriteLine(x + " " + a + " " + b + " " + c); |
|
} |
|
|
|
public void NestedTupleDeconstruction_Conversions((int, (int, int)) t) |
|
{ |
|
Console.WriteLine("NestedTupleDeconstruction_Conversions:"); |
|
(long x, (long a, long b)) = t; |
|
Console.WriteLine(x + " " + a + " " + b); |
|
} |
|
|
|
public void NestedTupleDeconstruction_ElementUsedOutside((int, (int, int)) t) |
|
{ |
|
Console.WriteLine("NestedTupleDeconstruction_ElementUsedOutside:"); |
|
var (x, inner) = t; |
|
Console.WriteLine(x + " " + inner.Item1 + " " + inner.Item2); |
|
} |
|
|
|
public void NestedTupleDeconstruction_ForEach(List<(int, (int, int))> list) |
|
{ |
|
Console.WriteLine("NestedTupleDeconstruction_ForEach:"); |
|
foreach (var (x, (a, b)) in list) |
|
{ |
|
Console.WriteLine(x + " " + a + " " + b); |
|
} |
|
} |
|
|
|
public class ConstrainedSource |
|
{ |
|
public int Value; |
|
|
|
public void Deconstruct(out int a, out NestedInner inner) |
|
{ |
|
Console.WriteLine("ConstrainedSource.Deconstruct"); |
|
a = Value; |
|
inner = new NestedInner { Value = Value * 10 }; |
|
} |
|
} |
|
|
|
public void NestedDeconstruction_SystemTupleSource(Tuple<int, NestedInner> tup) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_SystemTupleSource:"); |
|
(long x, (long a, long b)) = tup; |
|
int z = Side(); |
|
Console.WriteLine(x + " " + a + " " + b + " " + z); |
|
} |
|
|
|
public void NestedDeconstruction_CheckedConversions(NestedOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_CheckedConversions:"); |
|
checked |
|
{ |
|
(long x, (long a, long b)) = o; |
|
Console.WriteLine(x + " " + a + " " + b); |
|
} |
|
} |
|
|
|
public void NestedDeconstruction_GenericConstraintSource<T>(T o) where T : ConstrainedSource |
|
{ |
|
Console.WriteLine("NestedDeconstruction_GenericConstraintSource:"); |
|
var (a, (c, d)) = o; |
|
Console.WriteLine(a + " " + c + " " + d); |
|
} |
|
|
|
public void NestedDeconstruction_InParameterSource(in NestedOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_InParameterSource:"); |
|
(long x, (long a, long b)) = o; |
|
Console.WriteLine(x + " " + a + " " + b); |
|
} |
|
|
|
public void NestedDeconstruction_ConditionalSource(bool c, NestedOuter o1, NestedOuter o2) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_ConditionalSource:"); |
|
(long x, (int a, int b)) = c ? o1 : o2; |
|
int z = Side(); |
|
Console.WriteLine(x + " " + a + " " + b + " " + z); |
|
} |
|
|
|
public void NestedDeconstruction_TupleOuterConversions((int, NestedInner) tup) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_TupleOuterConversions:"); |
|
(long x, (long a, long b)) = tup; |
|
Console.WriteLine(x + " " + a + " " + b); |
|
} |
|
|
|
public void NestedDeconstruction_TypedConversions_UnrelatedCallAfter(NestedOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_TypedConversions_UnrelatedCallAfter:"); |
|
(long x, (long a, long b)) = o; |
|
int z = Side(); |
|
Console.WriteLine(x + " " + a + " " + b + " " + z); |
|
} |
|
|
|
public void NestedDeconstruction_NullableConversions(NestedOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_NullableConversions:"); |
|
(long? x, (long? a, int? b)) = o; |
|
Console.WriteLine(x + " " + a + " " + b); |
|
} |
|
|
|
public void NestedDeconstruction_MyIntConversionOnNestedLeaves(NestedOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_MyIntConversionOnNestedLeaves:"); |
|
(MyInt x, (MyInt a, long b)) = o; |
|
Console.WriteLine(x + " " + a + " " + b); |
|
} |
|
|
|
public void NestedDeconstruction_ForEachDictionary_Conversions(Dictionary<string, NestedInner> d) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_ForEachDictionary_Conversions:"); |
|
foreach ((string k, (long a, long b)) in d) |
|
{ |
|
Console.WriteLine(k + " " + a + " " + b); |
|
} |
|
} |
|
|
|
// The evaluation order of a deconstruction-assignment is: (1) all side-effects of |
|
// the left-hand-side targets, (2) all Deconstruct invocations, (3) conversions, |
|
// (4) assignments. Get(i), the Deconstruct methods, MyInt's implicit conversions, |
|
// and the property setters all print, so any phase reordering breaks the output diff. |
|
public void NestedDeconstruction_LhsSideEffects_DeconstructionOrder_Assignments() |
|
{ |
|
Console.WriteLine("NestedDeconstruction_LhsSideEffects_DeconstructionOrder_Assignments:"); |
|
(Get(0).IntProperty, (Get(1).IntProperty, Get(2).IntProperty)) = new NestedOuter { Value = 11 }; |
|
} |
|
|
|
public void NestedDeconstruction_Conversions_AfterAllDeconstructCalls() |
|
{ |
|
Console.WriteLine("NestedDeconstruction_Conversions_AfterAllDeconstructCalls:"); |
|
(Get(0).My, (Get(1).IntProperty, Get(2).My)) = new NestedOuter { Value = 21 }; |
|
} |
|
|
|
public void NestedDeconstruction_TypedDeclaration_Conversions(NestedOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_TypedDeclaration_Conversions:"); |
|
(MyInt x, (long a, MyInt b)) = o; |
|
Console.WriteLine(x); |
|
Console.WriteLine(a); |
|
Console.WriteLine(b); |
|
} |
|
|
|
public void NestedDeconstruction_DiscardWithSideEffectTargets() |
|
{ |
|
Console.WriteLine("NestedDeconstruction_DiscardWithSideEffectTargets:"); |
|
(Get(0).IntProperty, (_, Get(1).My)) = new NestedOuter { Value = 31 }; |
|
} |
|
|
|
public class HidingBase |
|
{ |
|
public int Value; |
|
|
|
public void Deconstruct(out string a, out double b) |
|
{ |
|
Console.WriteLine("HidingBase.Deconstruct"); |
|
a = "base" + Value; |
|
b = 0.5; |
|
} |
|
} |
|
|
|
public class HidingDerived : HidingBase |
|
{ |
|
public new void Deconstruct(out string a, out double b) |
|
{ |
|
Console.WriteLine("HidingDerived.Deconstruct"); |
|
a = "derived"; |
|
b = 99.5; |
|
} |
|
} |
|
|
|
public struct HidingOuter |
|
{ |
|
public void Deconstruct(out int x, out HidingDerived d) |
|
{ |
|
Console.WriteLine("HidingOuter.Deconstruct"); |
|
x = 1; |
|
d = new HidingDerived { Value = 5 }; |
|
} |
|
} |
|
|
|
// The base-typed view forces the call to bind to HidingBase.Deconstruct; a nested |
|
// designation cannot express that, because it rebinds on the element's static type, |
|
// where the hiding method wins. |
|
public void NestedDeconstruction_HiddenDeconstructMethod(HidingOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_HiddenDeconstructMethod:"); |
|
var (_, d) = o; |
|
HidingBase b = d; |
|
var (a, c) = b; |
|
Console.WriteLine(a); |
|
Console.WriteLine(c); |
|
} |
|
|
|
public int Side() |
|
{ |
|
Console.WriteLine("Side()"); |
|
return 5; |
|
} |
|
|
|
// A tuple deconstruction whose element is custom-deconstructed, followed by an |
|
// unrelated assignment: the tuple part must not be consumed into a pattern rooted |
|
// in the element's Deconstruct call. |
|
public void NestedDeconstruction_TupleWithCustomElement((int, NestedInner) tup) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_TupleWithCustomElement:"); |
|
var (x, (_, _)) = tup; |
|
int z = Side(); |
|
Console.WriteLine(x * x + z); |
|
} |
|
|
|
public class NestedClassInner |
|
{ |
|
public int Value; |
|
|
|
public void Deconstruct(out int a, out int b) |
|
{ |
|
Console.WriteLine("NestedClassInner.Deconstruct"); |
|
a = Value + 1; |
|
b = Value + 2; |
|
} |
|
} |
|
|
|
public struct ClassInnerOuter |
|
{ |
|
public int Value; |
|
|
|
public void Deconstruct(out int x, out NestedClassInner inner) |
|
{ |
|
Console.WriteLine("ClassInnerOuter.Deconstruct"); |
|
x = Value; |
|
inner = new NestedClassInner { Value = Value * 10 }; |
|
} |
|
} |
|
|
|
public void NestedDeconstruction_ClassInner(ClassInnerOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_ClassInner:"); |
|
var (x, (a, b)) = o; |
|
Console.WriteLine(x); |
|
Console.WriteLine(a); |
|
Console.WriteLine(b); |
|
} |
|
|
|
public struct DeepOuter |
|
{ |
|
public int Value; |
|
|
|
public void Deconstruct(out int x, out ClassInnerOuter mid) |
|
{ |
|
Console.WriteLine("DeepOuter.Deconstruct"); |
|
x = Value; |
|
mid = new ClassInnerOuter { Value = Value * 100 }; |
|
} |
|
} |
|
|
|
public void NestedDeconstruction_Depth3(DeepOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_Depth3:"); |
|
var (x, (y, (a, b))) = o; |
|
Console.WriteLine(x); |
|
Console.WriteLine(y); |
|
Console.WriteLine(a); |
|
Console.WriteLine(b); |
|
} |
|
|
|
public struct DiscardData |
|
{ |
|
public void Deconstruct(out object o1, out object o2) |
|
{ |
|
Console.WriteLine("DiscardData.Deconstruct"); |
|
o1 = 1; |
|
o2 = 2; |
|
} |
|
} |
|
|
|
public void NestedDeconstruction_DiscardedElement(KeyValuePair<object, DiscardData> pair) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_DiscardedElement:"); |
|
var (key, (value, _)) = pair; |
|
Console.WriteLine(key); |
|
Console.WriteLine(value); |
|
} |
|
|
|
public struct NestedInner |
|
{ |
|
public int Value; |
|
|
|
public void Deconstruct(out int a, out int b) |
|
{ |
|
Console.WriteLine("NestedInner.Deconstruct"); |
|
a = Value + 1; |
|
b = Value + 2; |
|
} |
|
} |
|
|
|
public struct NestedOuter |
|
{ |
|
public int Value; |
|
|
|
public void Deconstruct(out int x, out NestedInner inner) |
|
{ |
|
Console.WriteLine("NestedOuter.Deconstruct"); |
|
x = Value; |
|
inner = new NestedInner { Value = Value * 10 }; |
|
} |
|
} |
|
|
|
public void StructDeconstruction_Assignment(NestedInner s) |
|
{ |
|
Console.WriteLine("StructDeconstruction_Assignment:"); |
|
var (a, b) = s; |
|
Console.WriteLine(a); |
|
Console.WriteLine(b); |
|
} |
|
|
|
public void NestedDeconstruction_Assignment(NestedOuter o) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_Assignment:"); |
|
var (x, (a, b)) = o; |
|
Console.WriteLine(x); |
|
Console.WriteLine(a); |
|
Console.WriteLine(b); |
|
} |
|
|
|
public void NestedDeconstruction_ForEach(IEnumerable<NestedOuter> items) |
|
{ |
|
Console.WriteLine("NestedDeconstruction_ForEach:"); |
|
foreach (var (x, (a, b)) in items) |
|
{ |
|
Console.WriteLine(x + a + b); |
|
} |
|
} |
|
|
|
public void Property_NoDeconstruction_SwappedAssignments() |
|
{ |
|
Console.WriteLine("Property_NoDeconstruction_SwappedAssignments:"); |
|
AssignmentTargets customDeconstructionAndConversion = Get(0); |
|
AssignmentTargets customDeconstructionAndConversion2 = Get(1); |
|
GetSource<MyInt?, MyInt>().Deconstruct(out MyInt? x, out MyInt y); |
|
MyInt myInt2 = customDeconstructionAndConversion2.My = y; |
|
MyInt? myInt4 = customDeconstructionAndConversion.NMy = x; |
|
} |
|
|
|
public void Property_NoDeconstruction_SwappedInits() |
|
{ |
|
Console.WriteLine("Property_NoDeconstruction_SwappedInits:"); |
|
AssignmentTargets customDeconstructionAndConversion = Get(1); |
|
(Get(0).NMy, customDeconstructionAndConversion.My) = GetSource<MyInt?, MyInt>(); |
|
} |
|
|
|
public void Property_IntToUIntConversion() |
|
{ |
|
Console.WriteLine("Property_IntToUIntConversion:"); |
|
AssignmentTargets t0 = Get(0); |
|
AssignmentTargets t1 = Get(1); |
|
int a; |
|
uint b; |
|
GetSource<int, uint>().Deconstruct(out a, out b); |
|
t0.UIntProperty = (uint)a; |
|
t1.IntProperty = (int)b; |
|
} |
|
|
|
public void NoDeconstruction_NotUsingConver() |
|
{ |
|
Console.WriteLine("NoDeconstruction_NotUsingConver:"); |
|
AssignmentTargets t0 = Get(0); |
|
int a; |
|
uint b; |
|
GetSource<int, uint>().Deconstruct(out a, out b); |
|
long c = a; |
|
t0.IntProperty = a; |
|
t0.UIntProperty = b; |
|
Console.WriteLine(c); |
|
} |
|
|
|
public void NoDeconstruction_NotUsingConver_Tuple() |
|
{ |
|
Console.WriteLine("NoDeconstruction_NotUsingConver_Tuple:"); |
|
AssignmentTargets t0 = Get(0); |
|
var t = GetTuple<int, uint>(); |
|
long c = t.Item1; |
|
t0.IntProperty = t.Item1; |
|
t0.UIntProperty = t.Item2; |
|
Console.WriteLine(c); |
|
} |
|
|
|
public void NullReferenceException_Field_Deconstruction(out int a) |
|
{ |
|
try |
|
{ |
|
AssignmentTargets t0 = null; |
|
(t0.IntField, a) = GetSource<int, int>(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
a = 0; |
|
Console.WriteLine(ex.GetType().FullName); |
|
} |
|
} |
|
|
|
public void NullReferenceException_RefLocalReferencesField_Deconstruction(out int a) |
|
{ |
|
try |
|
{ |
|
AssignmentTargets t0 = null; |
|
ref int i = ref t0.IntField; |
|
(i, a) = GetSource<int, int>(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
a = 0; |
|
Console.WriteLine(ex.GetType().FullName); |
|
} |
|
} |
|
|
|
public void NullReferenceException_RefLocalReferencesArrayElement_Deconstruction(out int a, int[] arr) |
|
{ |
|
try |
|
{ |
|
ref int i = ref arr[0]; |
|
(i, a) = GetSource<int, int>(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
a = 0; |
|
Console.WriteLine(ex.GetType().FullName); |
|
} |
|
} |
|
|
|
public void DeconstructTupleSameVar((string, string) tuple) |
|
{ |
|
Console.WriteLine("DeconstructTupleSameVar:"); |
|
string a; |
|
a = tuple.Item1; |
|
a = tuple.Item2; |
|
Console.WriteLine(a); |
|
} |
|
|
|
public void DeconstructTupleListForEachSameVar(List<(string, string)> tuples) |
|
{ |
|
Console.WriteLine("DeconstructTupleListForEachSameVar:"); |
|
foreach (var tuple in tuples) |
|
{ |
|
string a; |
|
a = tuple.Item1; |
|
a = tuple.Item2; |
|
Console.WriteLine(a); |
|
} |
|
} |
|
} |
|
}
|
|
|