Skip to content

Commit 3ce8132

Browse files
h3xds1nzdipeshmsft
andauthored
Redefine Duration as readonly struct to prevent defensive copies (#9769)
* Convert Duration struct to readonly struct as it is immutable * Simplify Equals override * Simplify GetHashCode * Use nameof(timeSpan) in throw methods * Simplify TimeSpan property and remove unnecessary warning suppressions --------- Co-authored-by: Dipesh Kumar <[email protected]>
1 parent 075afff commit 3ce8132

File tree

2 files changed

+29
-54
lines changed

2 files changed

+29
-54
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Duration.cs

Lines changed: 28 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.ComponentModel;
6+
using System.Diagnostics;
67

78
namespace System.Windows
89
{
@@ -11,10 +12,10 @@ namespace System.Windows
1112
/// This structure may represent a TimeSpan, Automatic, or Forever value.
1213
/// </summary>
1314
[TypeConverter(typeof(DurationConverter))]
14-
public struct Duration
15+
public readonly struct Duration
1516
{
16-
private TimeSpan _timeSpan;
17-
private DurationType _durationType;
17+
private readonly TimeSpan _timeSpan;
18+
private readonly DurationType _durationType;
1819

1920
/// <summary>
2021
/// Creates a Duration from a TimeSpan.
@@ -23,13 +24,23 @@ public struct Duration
2324
public Duration(TimeSpan timeSpan)
2425
{
2526
if (timeSpan < TimeSpan.Zero)
26-
{
27-
throw new ArgumentException(SR.Timing_InvalidArgNonNegative, "timeSpan");
28-
}
27+
throw new ArgumentException(SR.Timing_InvalidArgNonNegative, nameof(timeSpan));
28+
2929
_durationType = DurationType.TimeSpan;
3030
_timeSpan = timeSpan;
3131
}
3232

33+
/// <summary>
34+
/// Private constructor, server for creation of <see cref="Duration.Automatic"/> and <see cref="Duration.Forever"/> only.
35+
/// </summary>
36+
/// <param name="durationType">Only <see cref="Duration.Automatic"/> and <see cref="Duration.Forever"/> values are permitted.</param>
37+
private Duration(DurationType durationType)
38+
{
39+
Debug.Assert(durationType == DurationType.Automatic || durationType == DurationType.Forever);
40+
41+
_durationType = durationType;
42+
}
43+
3344
#region Operators
3445

3546
//
@@ -39,7 +50,7 @@ public Duration(TimeSpan timeSpan)
3950
// Any comparision with Automatic returns false, except for ==.
4051
// Unlike NaN, Automatic == Automatic is true.
4152
//
42-
53+
4354

4455
/// <summary>
4556
/// Implicitly creates a Duration from a TimeSpan.
@@ -49,9 +60,8 @@ public Duration(TimeSpan timeSpan)
4960
public static implicit operator Duration(TimeSpan timeSpan)
5061
{
5162
if (timeSpan < TimeSpan.Zero)
52-
{
53-
throw new ArgumentException(SR.Timing_InvalidArgNonNegative, "timeSpan");
54-
}
63+
throw new ArgumentException(SR.Timing_InvalidArgNonNegative, nameof(timeSpan));
64+
5565
return new Duration(timeSpan);
5666
}
5767

@@ -346,7 +356,7 @@ public bool HasTimeSpan
346356
{
347357
get
348358
{
349-
return (_durationType == DurationType.TimeSpan);
359+
return _durationType == DurationType.TimeSpan;
350360
}
351361
}
352362

@@ -358,12 +368,7 @@ public static Duration Automatic
358368
{
359369
get
360370
{
361-
Duration duration = new Duration
362-
{
363-
_durationType = DurationType.Automatic
364-
};
365-
366-
return duration;
371+
return new Duration(DurationType.Automatic);
367372
}
368373
}
369374

@@ -375,32 +380,20 @@ public static Duration Forever
375380
{
376381
get
377382
{
378-
Duration duration = new Duration
379-
{
380-
_durationType = DurationType.Forever
381-
};
382-
383-
return duration;
383+
return new Duration(DurationType.Forever);
384384
}
385385
}
386386

387387
/// <summary>
388388
/// Returns the TimeSpan value that this Duration represents.
389389
/// </summary>
390390
/// <value>The TimeSpan value that this Duration represents.</value>
391-
/// <exception cref="System.InvalidOperationException">Thrown if this Duration represents null.</exception>
391+
/// <exception cref="InvalidOperationException">Thrown if this Duration represents null.</exception>
392392
public TimeSpan TimeSpan
393393
{
394394
get
395395
{
396-
if (HasTimeSpan)
397-
{
398-
return _timeSpan;
399-
}
400-
else
401-
{
402-
throw new InvalidOperationException(SR.Format(SR.Timing_NotTimeSpan, this));
403-
}
396+
return HasTimeSpan ? _timeSpan : throw new InvalidOperationException(SR.Format(SR.Timing_NotTimeSpan, this));
404397
}
405398
}
406399

@@ -423,20 +416,9 @@ public Duration Add(Duration duration)
423416
/// </summary>
424417
/// <param name="value"></param>
425418
/// <returns>true if value is a Duration and is equal to this instance; otherwise false.</returns>
426-
public override bool Equals(Object value)
419+
public override bool Equals(object value)
427420
{
428-
if (value == null)
429-
{
430-
return false;
431-
}
432-
else if (value is Duration)
433-
{
434-
return Equals((Duration)value);
435-
}
436-
else
437-
{
438-
return false;
439-
}
421+
return value is Duration duration && Equals(duration);
440422
}
441423

442424
/// <summary>
@@ -480,14 +462,7 @@ public static bool Equals(Duration t1, Duration t2)
480462
/// <returns>A 32-bit signed integer hash code.</returns>
481463
public override int GetHashCode()
482464
{
483-
if (HasTimeSpan)
484-
{
485-
return _timeSpan.GetHashCode();
486-
}
487-
else
488-
{
489-
return _durationType.GetHashCode() + 17;
490-
}
465+
return HasTimeSpan ? _timeSpan.GetHashCode() : _durationType.GetHashCode() + 17;
491466
}
492467

493468
/// <summary>

src/Microsoft.DotNet.Wpf/src/PresentationCore/ref/PresentationCore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ protected override void InvokeEventHandler(System.Delegate genericHandler, objec
651651
}
652652
public delegate void DragEventHandler(object sender, System.Windows.DragEventArgs e);
653653
[System.ComponentModel.TypeConverterAttribute(typeof(System.Windows.DurationConverter))]
654-
public partial struct Duration
654+
public readonly partial struct Duration
655655
{
656656
public Duration(System.TimeSpan timeSpan) { throw null; }
657657
public static System.Windows.Duration Automatic { get { throw null; } }

0 commit comments

Comments
 (0)