Skip to content

Commit fdcf567

Browse files
authored
add AsReadonly for IList and IDictionary (#150)
1 parent b1b0c98 commit fdcf567

File tree

7 files changed

+87
-15
lines changed

7 files changed

+87
-15
lines changed

api_list.include.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
* `Boolean TryGetValue<T>(T, T&)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trygetvalue)
2121

2222

23+
#### IDictionary<TKey,TValue>
24+
25+
* `Collections.ObjectModel.ReadOnlyDictionary<TKey,TValue> AsReadOnly<TKey, TValue>()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-2(system-collections-generic-idictionary((-0-1))))
26+
27+
2328
#### IEnumerable<TSource>
2429

2530
* `IEnumerable<KeyValuePair<TKey,TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(Func<TSource,TKey>, TAccumulate, Func<TAccumulate,TSource,TAccumulate>, IEqualityComparer<TKey>)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.aggregateby#system-linq-enumerable-aggregateby-3(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-func((-1-2))-system-func((-2-0-2))-system-collections-generic-iequalitycomparer((-1))))
@@ -35,6 +40,11 @@
3540
* `HashSet<TSource> ToHashSet<TSource>(IEqualityComparer<TSource>)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.tohashset#system-linq-enumerable-tohashset-1(system-collections-generic-ienumerable((-0))-system-collections-generic-iequalitycomparer((-0))))
3641

3742

43+
#### IList<T>
44+
45+
* `Collections.ObjectModel.ReadOnlyCollection<T> AsReadOnly<T>()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-1(system-collections-generic-ilist((-0))))
46+
47+
3848
#### IReadOnlyDictionary<TKey,TValue>
3949

4050
* `TValue GetValueOrDefault<TKey, TValue>(TKey)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.getvalueordefault)

readme.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,11 @@ The class `Polyfill` includes the following extension methods:
377377
* `Boolean TryGetValue<T>(T, T&)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trygetvalue)
378378

379379

380+
#### IDictionary<TKey,TValue>
381+
382+
* `Collections.ObjectModel.ReadOnlyDictionary<TKey,TValue> AsReadOnly<TKey, TValue>()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-2(system-collections-generic-idictionary((-0-1))))
383+
384+
380385
#### IEnumerable<TSource>
381386

382387
* `IEnumerable<KeyValuePair<TKey,TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(Func<TSource,TKey>, TAccumulate, Func<TAccumulate,TSource,TAccumulate>, IEqualityComparer<TKey>)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.aggregateby#system-linq-enumerable-aggregateby-3(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-func((-1-2))-system-func((-2-0-2))-system-collections-generic-iequalitycomparer((-1))))
@@ -392,6 +397,11 @@ The class `Polyfill` includes the following extension methods:
392397
* `HashSet<TSource> ToHashSet<TSource>(IEqualityComparer<TSource>)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.tohashset#system-linq-enumerable-tohashset-1(system-collections-generic-ienumerable((-0))-system-collections-generic-iequalitycomparer((-0))))
393398

394399

400+
#### IList<T>
401+
402+
* `Collections.ObjectModel.ReadOnlyCollection<T> AsReadOnly<T>()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-1(system-collections-generic-ilist((-0))))
403+
404+
395405
#### IReadOnlyDictionary<TKey,TValue>
396406

397407
* `TValue GetValueOrDefault<TKey, TValue>(TKey)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.getvalueordefault)

src/Consume/Consume.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ class Consume
7575
var startsWith = "value".StartsWith('a');
7676
var endsWith = "value".EndsWith('a');
7777

78+
IList<string> ilist = new List<string>();
79+
ilist.AsReadOnly();
80+
81+
IDictionary<int, int> idictionary = new Dictionary<int, int>();
82+
idictionary.AsReadOnly();
83+
7884
var enumerable = (IEnumerable<string>) new List<string>
7985
{
8086
"a",

src/Polyfill/Polyfill_Dictionary.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,28 @@
22

33
#pragma warning disable
44

5-
#if NETFRAMEWORK || NETSTANDARD2_0 || NETCOREAPP2X
6-
75
using System;
86
using System.Collections.Generic;
7+
using System.Collections.ObjectModel;
98
using System.Diagnostics.CodeAnalysis;
109
using Link = System.ComponentModel.DescriptionAttribute;
1110

1211
static partial class Polyfill
1312
{
13+
#if !NET7_0_OR_GREATER
14+
/// <summary>
15+
/// Returns a read-only <see cref="ReadOnlyDictionary{TKey,TValue}"/> wrapper for the current dictionary.
16+
/// </summary>
17+
/// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
18+
/// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
19+
/// <param name="dictionary">The dictionary to wrap.</param>
20+
/// <returns>An object that acts as a read-only wrapper around the current <see cref="IDictionary{TKey, TValue}"/>.</returns>
21+
/// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is null.</exception>
22+
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-2(system-collections-generic-idictionary((-0-1)))")]
23+
public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(this IDictionary<TKey, TValue> target) =>
24+
new(target);
25+
#endif
26+
#if NETFRAMEWORK || NETSTANDARD2_0 || NETCOREAPP2X
1427
/// <summary>
1528
/// Removes the value with the specified key from the <see cref="Dictionary{TKey,TValue}"/>, and copies the element
1629
/// to the value parameter.
@@ -24,12 +37,12 @@ static partial class Polyfill
2437
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <code>null</code>.</exception>
2538
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.remove")]
2639
public static bool Remove<TKey, TValue>(
27-
this Dictionary<TKey,TValue> target,
40+
this Dictionary<TKey, TValue> target,
2841
TKey key,
2942
[MaybeNullWhen(false)] out TValue value)
3043
{
3144
target.TryGetValue(key, out value);
3245
return target.Remove(key);
3346
}
34-
}
35-
#endif
47+
#endif
48+
}

src/Polyfill/Polyfill_List.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,35 @@
22

33
#pragma warning disable
44

5-
#if !NET8_0_OR_GREATER && FeatureMemory
65

76
using System;
87
using System.Collections.Generic;
8+
using System.Collections.ObjectModel;
99
using Link = System.ComponentModel.DescriptionAttribute;
1010

1111
static partial class Polyfill
1212
{
13+
#if !NET7_0_OR_GREATER
14+
15+
/// <summary>Returns a read-only <see cref="ReadOnlyCollection{T}" /> wrapper for the current collection.</summary>
16+
/// <returns>An object that acts as a read-only wrapper around the current <see cref="IList{T}" />.</returns>
17+
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-1(system-collections-generic-ilist((-0)))")]
18+
public static ReadOnlyCollection<T> AsReadOnly<T>(this IList<T> target) =>
19+
new(target);
20+
#endif
21+
22+
#if !NET8_0_OR_GREATER && FeatureMemory
1323
/// <summary>Adds the elements of the specified span to the end of the <see cref="List{T}"/>.</summary>
1424
/// <typeparam name="T">The type of elements in the list.</typeparam>
1525
/// <param name="list">The list to which the elements should be added.</param>
1626
/// <param name="source">The span whose elements should be added to the end of the <see cref="List{T}"/>.</param>
1727
/// <exception cref="ArgumentNullException">The <paramref name="list"/> is null.</exception>
1828
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.addrange")]
19-
public static void AddRange<T>(this List<T> list, ReadOnlySpan<T> source)
29+
public static void AddRange<T>(this List<T> target, ReadOnlySpan<T> source)
2030
{
2131
foreach (var item in source)
2232
{
23-
list.Add(item);
33+
target.Add(item);
2434
}
2535
}
2636

@@ -32,12 +42,12 @@ public static void AddRange<T>(this List<T> list, ReadOnlySpan<T> source)
3242
/// <exception cref="ArgumentNullException">The <paramref name="list"/> is null.</exception>
3343
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0 or greater than <paramref name="list"/>'s <see cref="List{T}.Count"/>.</exception>
3444
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.insertrange")]
35-
public static void InsertRange<T>(this List<T> list, int index, ReadOnlySpan<T> source)
45+
public static void InsertRange<T>(this List<T> target, int index, ReadOnlySpan<T> source)
3646
{
3747
for (var i = 0; i < source.Length; i++)
3848
{
3949
var item = source[i];
40-
list.Insert(i + index, item);
50+
target.Insert(i + index, item);
4151
}
4252
}
4353

@@ -48,12 +58,12 @@ public static void InsertRange<T>(this List<T> list, int index, ReadOnlySpan<T>
4858
/// <exception cref="ArgumentNullException">The <paramref name="list"/> is null.</exception>
4959
/// <exception cref="ArgumentException">The number of elements in the source <see cref="List{T}"/> is greater than the number of elements that the destination span can contain.</exception>
5060
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.copyto?view=net-8.0")]
51-
public static void CopyTo<T>(this List<T> list, Span<T> destination)
61+
public static void CopyTo<T>(this List<T> target, Span<T> destination)
5262
{
53-
for (var index = 0; index < list.Count; index++)
63+
for (var index = 0; index < target.Count; index++)
5464
{
55-
destination[index] = list[index];
65+
destination[index] = target[index];
5666
}
5767
}
58-
}
59-
#endif
68+
#endif
69+
}

src/Tests/PolyfillTests_Dictionary.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
partial class PolyfillTests
22
{
3+
[Test]
4+
public void IReadOnlyDictionaryAsReadOnly()
5+
{
6+
IDictionary<string, string> dictionary = new Dictionary<string,string>
7+
{
8+
{"key", "value"}
9+
};
10+
11+
var readOnly = dictionary.AsReadOnly();
12+
Assert.AreEqual("value", readOnly["key"]);
13+
}
14+
315
[Test]
416
public void Dictionary_Remove()
517
{

src/Tests/PolyfillTests_List.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
partial class PolyfillTests
22
{
3+
[Test]
4+
public void IListAsReadOnly()
5+
{
6+
IList<char> list = new List<char>
7+
{
8+
'a'
9+
};
10+
var readOnly = list.AsReadOnly();
11+
Assert.AreEqual('a', readOnly[0]);
12+
}
13+
314
[Test]
415
public void ListAddRangeReadOnlySpan()
516
{

0 commit comments

Comments
 (0)