-
Notifications
You must be signed in to change notification settings - Fork 119
/
ReadOnlyListView.cs
106 lines (92 loc) · 4.82 KB
/
ReadOnlyListView.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DotNext.Collections.Generic;
/// <summary>
/// Represents lazily converted read-only list.
/// </summary>
/// <typeparam name="TInput">Type of items in the source list.</typeparam>
/// <typeparam name="TOutput">Type of items in the converted list.</typeparam>
/// <remarks>
/// Initializes a new lazily converted view.
/// </remarks>
/// <param name="list">Read-only list to convert.</param>
/// <param name="mapper">List items converter.</param>
[StructLayout(LayoutKind.Auto)]
public readonly struct ReadOnlyListView<TInput, TOutput>(IReadOnlyList<TInput> list, Func<TInput, TOutput> mapper) : IReadOnlyList<TOutput>, IEquatable<ReadOnlyListView<TInput, TOutput>>
{
private readonly IReadOnlyList<TInput>? source = list ?? throw new ArgumentNullException(nameof(list));
private readonly Func<TInput, TOutput> mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
/// <summary>
/// Initializes a new lazily converted view.
/// </summary>
/// <param name="list">Read-only list to convert.</param>
/// <param name="mapper">List items converter.</param>
public ReadOnlyListView(IReadOnlyList<TInput> list, Converter<TInput, TOutput> mapper)
: this(list, Unsafe.As<Func<TInput, TOutput>>(mapper))
{
}
/// <summary>
/// Gets item at the specified position.
/// </summary>
/// <param name="index">Zero-based index of the item.</param>
/// <returns>Converted item at the specified position.</returns>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
public TOutput this[int index]
{
get
{
if (source is null)
throw new ArgumentOutOfRangeException(nameof(index));
return mapper.Invoke(source[index]);
}
}
/// <summary>
/// Count of items in the list.
/// </summary>
public int Count => source?.Count ?? 0;
/// <summary>
/// Returns enumerator over converted items.
/// </summary>
/// <returns>The enumerator over converted items.</returns>
public IEnumerator<TOutput> GetEnumerator()
=> source is null or { Count: 0 } || mapper is null ? Enumerable.Empty<TOutput>().GetEnumerator() : source.Select(mapper).GetEnumerator();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private bool Equals(in ReadOnlyListView<TInput, TOutput> other)
=> ReferenceEquals(source, other.source) && mapper == other.mapper;
/// <summary>
/// Determines whether two converted lists are same.
/// </summary>
/// <param name="other">Other list to compare.</param>
/// <returns><see langword="true"/> if this view wraps the same source list and contains the same converter as other view; otherwise, <see langword="false"/>.</returns>
public bool Equals(ReadOnlyListView<TInput, TOutput> other) => Equals(in other);
/// <summary>
/// Returns hash code for the this list.
/// </summary>
/// <returns>The hash code of this list.</returns>
public override int GetHashCode() => RuntimeHelpers.GetHashCode(source) ^ mapper.GetHashCode();
/// <summary>
/// Determines whether two converted lists are same.
/// </summary>
/// <param name="other">Other list to compare.</param>
/// <returns><see langword="true"/> if this collection wraps the same source collection and contains the same converter as other collection; otherwise, <see langword="false"/>.</returns>
public override bool Equals(object? other)
=> other is ReadOnlyListView<TInput, TOutput> view ? Equals(in view) : Equals(source, other);
/// <summary>
/// Determines whether two views are same.
/// </summary>
/// <param name="first">The first list view to compare.</param>
/// <param name="second">The second list view to compare.</param>
/// <returns><see langword="true"/> if the first view wraps the same source collection and contains the same converter as the second view; otherwise, <see langword="false"/>.</returns>
public static bool operator ==(in ReadOnlyListView<TInput, TOutput> first, in ReadOnlyListView<TInput, TOutput> second)
=> first.Equals(in second);
/// <summary>
/// Determines whether two views are not same.
/// </summary>
/// <param name="first">The first list view to compare.</param>
/// <param name="second">The second list view to compare.</param>
/// <returns><see langword="true"/> if the first view wraps the different source collection and contains the different converter as the second view; otherwise, <see langword="false"/>.</returns>
public static bool operator !=(in ReadOnlyListView<TInput, TOutput> first, in ReadOnlyListView<TInput, TOutput> second)
=> !first.Equals(in second);
}