Remove use of Tuple<> from corelib (#44706)
authorStephen Toub <stoub@microsoft.com>
Tue, 17 Nov 2020 07:23:52 +0000 (02:23 -0500)
committerGitHub <noreply@github.com>
Tue, 17 Nov 2020 07:23:52 +0000 (23:23 -0800)
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs
src/libraries/System.Private.CoreLib/src/System/IO/TextReader.cs
src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs
src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
src/libraries/System.Private.CoreLib/src/System/TupleSlim.cs [new file with mode: 0644]

index 5ac28c7..b0ad2cf 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.TransitionTime.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneNotFoundException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Tuple.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\TupleSlim.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\TupleExtensions.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Type.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Type.Enum.cs" />
index 99fd304..d66b9f4 100644 (file)
@@ -82,7 +82,7 @@ namespace System.Diagnostics.Tracing
         private byte m_level;                            // Tracing Level
         private long m_anyKeywordMask;                   // Trace Enable Flags
         private long m_allKeywordMask;                   // Match all keyword
-        private List<SessionInfo>? m_liveSessions;       // current live sessions (Tuple<sessionIdBit, etwSessionId>)
+        private List<SessionInfo>? m_liveSessions;       // current live sessions (KeyValuePair<sessionIdBit, etwSessionId>)
         private bool m_enabled;                          // Enabled flag from Trace callback
         private string? m_providerName;                  // Control name
         private Guid m_providerId;                       // Control Guid
@@ -256,7 +256,7 @@ namespace System.Diagnostics.Tracing
                     m_anyKeywordMask = anyKeyword;
                     m_allKeywordMask = allKeyword;
 
-                    List<Tuple<SessionInfo, bool>> sessionsChanged = GetSessions();
+                    List<KeyValuePair<SessionInfo, bool>> sessionsChanged = GetSessions();
 
                     // The GetSessions() logic was here to support the idea that different ETW sessions
                     // could have different user-defined filters.   (I believe it is currently broken but that is another matter.)
@@ -268,13 +268,13 @@ namespace System.Diagnostics.Tracing
                     // All this session based logic should be reviewed and likely removed, but that is a larger
                     // change that needs more careful staging.
                     if (sessionsChanged.Count == 0)
-                        sessionsChanged.Add(new Tuple<SessionInfo, bool>(new SessionInfo(0, 0), true));
+                        sessionsChanged.Add(new KeyValuePair<SessionInfo, bool>(new SessionInfo(0, 0), true));
 
-                    foreach (Tuple<SessionInfo, bool> session in sessionsChanged)
+                    foreach (KeyValuePair<SessionInfo, bool> session in sessionsChanged)
                     {
-                        int sessionChanged = session.Item1.sessionIdBit;
-                        int etwSessionId = session.Item1.etwSessionId;
-                        bool bEnabling = session.Item2;
+                        int sessionChanged = session.Key.sessionIdBit;
+                        int etwSessionId = session.Key.etwSessionId;
+                        bool bEnabling = session.Value;
 
                         skipFinalOnControllerCommand = true;
                         args = null;                                // reinitialize args for every session...
@@ -374,7 +374,7 @@ namespace System.Diagnostics.Tracing
         /// ETW session that was added or remove, and the bool specifies whether the
         /// session was added or whether it was removed from the set.
         /// </summary>
-        private List<Tuple<SessionInfo, bool>> GetSessions()
+        private List<KeyValuePair<SessionInfo, bool>> GetSessions()
         {
             List<SessionInfo>? liveSessionList = null;
 
@@ -383,7 +383,7 @@ namespace System.Diagnostics.Tracing
                     GetSessionInfoCallback(etwSessionId, matchAllKeywords, ref sessionList),
                 ref liveSessionList);
 
-            List<Tuple<SessionInfo, bool>> changedSessionList = new List<Tuple<SessionInfo, bool>>();
+            List<KeyValuePair<SessionInfo, bool>> changedSessionList = new List<KeyValuePair<SessionInfo, bool>>();
 
             // first look for sessions that have gone away (or have changed)
             // (present in the m_liveSessions but not in the new liveSessionList)
@@ -394,7 +394,7 @@ namespace System.Diagnostics.Tracing
                     int idx;
                     if ((idx = IndexOfSessionInList(liveSessionList, s.etwSessionId)) < 0 ||
                         (liveSessionList![idx].sessionIdBit != s.sessionIdBit))
-                        changedSessionList.Add(Tuple.Create(s, false));
+                        changedSessionList.Add(new KeyValuePair<SessionInfo, bool>(s, false));
                 }
             }
             // next look for sessions that were created since the last callback  (or have changed)
@@ -406,7 +406,7 @@ namespace System.Diagnostics.Tracing
                     int idx;
                     if ((idx = IndexOfSessionInList(m_liveSessions, s.etwSessionId)) < 0 ||
                         (m_liveSessions![idx].sessionIdBit != s.sessionIdBit))
-                        changedSessionList.Add(Tuple.Create(s, true));
+                        changedSessionList.Add(new KeyValuePair<SessionInfo, bool>(s, true));
                 }
             }
 
index 8321c52..05211d6 100644 (file)
@@ -249,20 +249,16 @@ namespace System.IO
                 ReadAsync(array.Array!, array.Offset, array.Count) :
                 Task<int>.Factory.StartNew(static state =>
                 {
-                    var t = (Tuple<TextReader, Memory<char>>)state!;
+                    var t = (TupleSlim<TextReader, Memory<char>>)state!;
                     return t.Item1.Read(t.Item2.Span);
-                }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+                }, new TupleSlim<TextReader, Memory<char>>(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
 
-        internal virtual ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
-        {
-            var tuple = new Tuple<TextReader, Memory<char>>(this, buffer);
-            return new ValueTask<int>(Task<int>.Factory.StartNew(static state =>
+        internal virtual ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken) =>
+            new ValueTask<int>(Task<int>.Factory.StartNew(static state =>
             {
-                var t = (Tuple<TextReader, Memory<char>>)state!;
+                var t = (TupleSlim<TextReader, Memory<char>>)state!;
                 return t.Item1.Read(t.Item2.Span);
-            },
-            tuple, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
-        }
+            }, new TupleSlim<TextReader, Memory<char>>(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
 
         public virtual Task<int> ReadBlockAsync(char[] buffer, int index, int count)
         {
@@ -287,9 +283,9 @@ namespace System.IO
                 ReadBlockAsync(array.Array!, array.Offset, array.Count) :
                 Task<int>.Factory.StartNew(static state =>
                 {
-                    var t = (Tuple<TextReader, Memory<char>>)state!;
+                    var t = (TupleSlim<TextReader, Memory<char>>)state!;
                     return t.Item1.ReadBlock(t.Item2.Span);
-                }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+                }, new TupleSlim<TextReader, Memory<char>>(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
 
         internal async ValueTask<int> ReadBlockAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
         {
index 65e9428..762d188 100644 (file)
@@ -538,27 +538,19 @@ namespace System.IO
         }
 
         #region Task based Async APIs
-        public virtual Task WriteAsync(char value)
-        {
-            var tuple = new Tuple<TextWriter, char>(this, value);
-            return Task.Factory.StartNew(static state =>
+        public virtual Task WriteAsync(char value) =>
+            Task.Factory.StartNew(static state =>
             {
-                var t = (Tuple<TextWriter, char>)state!;
+                var t = (TupleSlim<TextWriter, char>)state!;
                 t.Item1.Write(t.Item2);
-            },
-            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-        }
+            }, new TupleSlim<TextWriter, char>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
-        public virtual Task WriteAsync(string? value)
-        {
-            var tuple = new Tuple<TextWriter, string?>(this, value);
-            return Task.Factory.StartNew(static state =>
+        public virtual Task WriteAsync(string? value) =>
+            Task.Factory.StartNew(static state =>
             {
-                var t = (Tuple<TextWriter, string?>)state!;
+                var t = (TupleSlim<TextWriter, string?>)state!;
                 t.Item1.Write(t.Item2);
-            },
-            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-        }
+            }, new TupleSlim<TextWriter, string?>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
         /// <summary>
         /// Equivalent to WriteAsync(stringBuilder.ToString()) however it uses the
@@ -592,16 +584,12 @@ namespace System.IO
             return WriteAsync(buffer, 0, buffer.Length);
         }
 
-        public virtual Task WriteAsync(char[] buffer, int index, int count)
-        {
-            var tuple = new Tuple<TextWriter, char[], int, int>(this, buffer, index, count);
-            return Task.Factory.StartNew(static state =>
+        public virtual Task WriteAsync(char[] buffer, int index, int count) =>
+            Task.Factory.StartNew(static state =>
             {
-                var t = (Tuple<TextWriter, char[], int, int>)state!;
+                var t = (TupleSlim<TextWriter, char[], int, int>)state!;
                 t.Item1.Write(t.Item2, t.Item3, t.Item4);
-            },
-            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-        }
+            }, new TupleSlim<TextWriter, char[], int, int>(this, buffer, index, count), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
         public virtual Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) =>
             cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
@@ -609,31 +597,23 @@ namespace System.IO
                 WriteAsync(array.Array!, array.Offset, array.Count) :
                 Task.Factory.StartNew(static state =>
                 {
-                    var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state!;
+                    var t = (TupleSlim<TextWriter, ReadOnlyMemory<char>>)state!;
                     t.Item1.Write(t.Item2.Span);
-                }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+                }, new TupleSlim<TextWriter, ReadOnlyMemory<char>>(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
-        public virtual Task WriteLineAsync(char value)
-        {
-            var tuple = new Tuple<TextWriter, char>(this, value);
-            return Task.Factory.StartNew(static state =>
+        public virtual Task WriteLineAsync(char value) =>
+            Task.Factory.StartNew(static state =>
             {
-                var t = (Tuple<TextWriter, char>)state!;
+                var t = (TupleSlim<TextWriter, char>)state!;
                 t.Item1.WriteLine(t.Item2);
-            },
-            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-        }
+            }, new TupleSlim<TextWriter, char>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
-        public virtual Task WriteLineAsync(string? value)
-        {
-            var tuple = new Tuple<TextWriter, string?>(this, value);
-            return Task.Factory.StartNew(static state =>
+        public virtual Task WriteLineAsync(string? value) =>
+            Task.Factory.StartNew(static state =>
             {
-                var t = (Tuple<TextWriter, string?>)state!;
+                var t = (TupleSlim<TextWriter, string?>)state!;
                 t.Item1.WriteLine(t.Item2);
-            },
-            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-        }
+            }, new TupleSlim<TextWriter, string?>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
         /// <summary>
         /// Equivalent to WriteLineAsync(stringBuilder.ToString()) however it uses the
@@ -668,16 +648,12 @@ namespace System.IO
             return WriteLineAsync(buffer, 0, buffer.Length);
         }
 
-        public virtual Task WriteLineAsync(char[] buffer, int index, int count)
-        {
-            var tuple = new Tuple<TextWriter, char[], int, int>(this, buffer, index, count);
-            return Task.Factory.StartNew(static state =>
+        public virtual Task WriteLineAsync(char[] buffer, int index, int count) =>
+            Task.Factory.StartNew(static state =>
             {
-                var t = (Tuple<TextWriter, char[], int, int>)state!;
+                var t = (TupleSlim<TextWriter, char[], int, int>)state!;
                 t.Item1.WriteLine(t.Item2, t.Item3, t.Item4);
-            },
-            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-        }
+            }, new TupleSlim<TextWriter, char[], int, int>(this, buffer, index, count), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
         public virtual Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) =>
             cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
@@ -685,9 +661,9 @@ namespace System.IO
                 WriteLineAsync(array.Array!, array.Offset, array.Count) :
                 Task.Factory.StartNew(static state =>
                 {
-                    var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state!;
+                    var t = (TupleSlim<TextWriter, ReadOnlyMemory<char>>)state!;
                     t.Item1.WriteLine(t.Item2.Span);
-                }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+                }, new TupleSlim<TextWriter, ReadOnlyMemory<char>>(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
         public virtual Task WriteLineAsync()
         {
index 9fb58d4..19357cd 100644 (file)
@@ -826,10 +826,9 @@ namespace System.Threading
             // this work with a callback mechanism will add additional cost to other more common cases.
             return new ValueTask(Task.Factory.StartNew(static s =>
             {
-                Debug.Assert(s is Tuple<CancellationTokenSource, long>);
-                var state = (Tuple<CancellationTokenSource, long>)s;
+                var state = (TupleSlim<CancellationTokenSource, long>)s!;
                 state.Item1.WaitForCallbackToComplete(state.Item2);
-            }, Tuple.Create(this, id), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default));
+            }, new TupleSlim<CancellationTokenSource, long>(this, id), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default));
         }
 
         private sealed class Linked1CancellationTokenSource : CancellationTokenSource
index 505fd06..e354b58 100644 (file)
@@ -501,11 +501,9 @@ namespace System.Threading.Tasks
         /// A scheduler shim used to queue tasks to the pair and execute those tasks on request of the pair.
         /// </summary>
         [DebuggerDisplay("Count={CountForDebugger}, MaxConcurrencyLevel={m_maxConcurrencyLevel}, Id={Id}")]
-        [DebuggerTypeProxy(typeof(ConcurrentExclusiveTaskScheduler.DebugView))]
+        [DebuggerTypeProxy(typeof(DebugView))]
         private sealed class ConcurrentExclusiveTaskScheduler : TaskScheduler
         {
-            /// <summary>Cached delegate for invoking TryExecuteTaskShim.</summary>
-            private static readonly Func<object?, bool> s_tryExecuteTaskShim = new Func<object?, bool>(TryExecuteTaskShim);
             /// <summary>The parent pair.</summary>
             private readonly ConcurrentExclusiveSchedulerPair m_pair;
             /// <summary>The maximum concurrency level for the scheduler.</summary>
@@ -632,7 +630,11 @@ namespace System.Threading.Tasks
                 // is able to invoke the task, which might account for an additional but unavoidable delay.
                 // Once it's done, we can return whether the task executed by returning the
                 // shim task's Result, which is in turn the result of TryExecuteTask.
-                var t = new Task<bool>(s_tryExecuteTaskShim, Tuple.Create(this, task));
+                var t = new Task<bool>(s =>
+                {
+                    var tuple = (TupleSlim<ConcurrentExclusiveTaskScheduler, Task>)s!;
+                    return tuple.Item1.TryExecuteTask(tuple.Item2);
+                }, new TupleSlim<ConcurrentExclusiveTaskScheduler, Task>(this, task));
                 try
                 {
                     t.RunSynchronously(m_pair.m_underlyingTaskScheduler);
@@ -647,20 +649,6 @@ namespace System.Threading.Tasks
                 finally { t.Dispose(); }
             }
 
-            /// <summary>Shim used to invoke this.TryExecuteTask(task).</summary>
-            /// <param name="state">A tuple of the ConcurrentExclusiveTaskScheduler and the task to execute.</param>
-            /// <returns>true if the task was successfully inlined; otherwise, false.</returns>
-            /// <remarks>
-            /// This method is separated out not because of performance reasons but so that
-            /// the SecuritySafeCritical attribute may be employed.
-            /// </remarks>
-            private static bool TryExecuteTaskShim(object? state)
-            {
-                Debug.Assert(state is Tuple<ConcurrentExclusiveTaskScheduler, Task>);
-                var tuple = (Tuple<ConcurrentExclusiveTaskScheduler, Task>)state;
-                return tuple.Item1.TryExecuteTask(tuple.Item2);
-            }
-
             /// <summary>Gets for debugging purposes the tasks scheduled to this scheduler.</summary>
             /// <returns>An enumerable of the tasks queued.</returns>
             protected override IEnumerable<Task> GetScheduledTasks() { return m_tasks; }
index 647e4d7..846e755 100644 (file)
@@ -175,9 +175,9 @@ namespace System.Threading.Tasks.Sources
                     case SynchronizationContext sc:
                         sc.Post(static s =>
                         {
-                            var tuple = (Tuple<Action<object?>, object?>)s!;
+                            var tuple = (TupleSlim<Action<object?>, object?>)s!;
                             tuple.Item1(tuple.Item2);
-                        }, Tuple.Create(continuation, state));
+                        }, new TupleSlim<Action<object?>, object?>(continuation, state));
                         break;
 
                     case TaskScheduler ts:
@@ -320,9 +320,9 @@ namespace System.Threading.Tasks.Sources
                 case SynchronizationContext sc:
                     sc.Post(static s =>
                     {
-                        var state = (Tuple<Action<object?>, object?>)s!;
+                        var state = (TupleSlim<Action<object?>, object?>)s!;
                         state.Item1(state.Item2);
-                    }, Tuple.Create(_continuation, _continuationState));
+                    }, new TupleSlim<Action<object?>, object?>(_continuation, _continuationState));
                     break;
 
                 case TaskScheduler ts:
index b4694ee..201e316 100644 (file)
@@ -634,15 +634,14 @@ namespace System.Threading.Tasks
                             // antecedent.RemoveCancellation(continuation) can be invoked.
                             ctr = cancellationToken.UnsafeRegister(static t =>
                             {
-                                var tuple = (Tuple<Task, Task, TaskContinuation>)t!;
+                                var tuple = (TupleSlim<Task, Task, TaskContinuation>)t!;
 
                                 Task targetTask = tuple.Item1;
                                 Task antecedentTask = tuple.Item2;
 
                                 antecedentTask.RemoveContinuation(tuple.Item3);
                                 targetTask.InternalCancel();
-                            },
-                            new Tuple<Task, Task, TaskContinuation>(this, antecedent, continuation));
+                            }, new TupleSlim<Task, Task, TaskContinuation>(this, antecedent, continuation));
                         }
 
                         props.m_cancellationRegistration = new StrongBox<CancellationTokenRegistration>(ctr);
@@ -6667,9 +6666,9 @@ namespace System.Threading.Tasks
             ThreadPool.UnsafeQueueUserWorkItem(static state =>
             {
                 // InvokeCore(completingTask);
-                var tuple = (Tuple<UnwrapPromise<TResult>, Task>)state!;
+                var tuple = (TupleSlim<UnwrapPromise<TResult>, Task>)state!;
                 tuple.Item1.InvokeCore(tuple.Item2);
-            }, Tuple.Create<UnwrapPromise<TResult>, Task>(this, completingTask));
+            }, new TupleSlim<UnwrapPromise<TResult>, Task>(this, completingTask));
         }
 
         /// <summary>Processes the outer task once it's completed.</summary>
diff --git a/src/libraries/System.Private.CoreLib/src/System/TupleSlim.cs b/src/libraries/System.Private.CoreLib/src/System/TupleSlim.cs
new file mode 100644 (file)
index 0000000..250008a
--- /dev/null
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System
+{
+    /// <summary>Simple alternative to <see cref="Tuple{T1, T2}"/> for use in corelib.</summary>
+    /// <remarks>Exists to avoid the unnecessary size increase that may come from Tuple's additional surface area.</remarks>
+    internal sealed class TupleSlim<T1, T2>
+    {
+        public readonly T1 Item1;
+        public readonly T2 Item2;
+
+        public TupleSlim(T1 item1, T2 item2)
+        {
+            Item1 = item1;
+            Item2 = item2;
+        }
+    }
+
+    /// <summary>Simple alternative to <see cref="Tuple{T1, T2, T3}"/> for use in corelib.</summary>
+    /// <remarks>Exists to avoid the unnecessary size increase that may come from Tuple's additional surface area.</remarks>
+    internal sealed class TupleSlim<T1, T2, T3>
+    {
+        public readonly T1 Item1;
+        public readonly T2 Item2;
+        public readonly T3 Item3;
+
+        public TupleSlim(T1 item1, T2 item2, T3 item3)
+        {
+            Item1 = item1;
+            Item2 = item2;
+            Item3 = item3;
+        }
+    }
+
+    /// <summary>Simple alternative to <see cref="Tuple{T1, T2, T3, T4}"/> for use in corelib.</summary>
+    /// <remarks>Exists to avoid the unnecessary size increase that may come from Tuple's additional surface area.</remarks>
+    internal sealed class TupleSlim<T1, T2, T3, T4>
+    {
+        public readonly T1 Item1;
+        public readonly T2 Item2;
+        public readonly T3 Item3;
+        public readonly T4 Item4;
+
+        public TupleSlim(T1 item1, T2 item2, T3 item3, T4 item4)
+        {
+            Item1 = item1;
+            Item2 = item2;
+            Item3 = item3;
+            Item4 = item4;
+        }
+    }
+}