Skip to content

Commit 5d0d3bf

Browse files
committed
Improved nullability annotations in messenger types
1 parent 7e63b1e commit 5d0d3bf

File tree

4 files changed

+51
-35
lines changed

4 files changed

+51
-35
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if NETSTANDARD2_0
6+
7+
namespace System.Diagnostics.CodeAnalysis
8+
{
9+
/// <summary>
10+
/// Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.
11+
/// </summary>
12+
/// <remarks>Internal copy from the BCL attribute.</remarks>
13+
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
14+
internal sealed class NotNullWhenAttribute : Attribute
15+
{
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="NotNullWhenAttribute"/> class.
18+
/// </summary>
19+
/// <param name="returnValue">The return value condition. If the method returns this value, the associated parameter will not be null.</param>
20+
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
21+
22+
/// <summary>
23+
/// Gets a value indicating whether the annotated variable is not <see langword="null"/>.
24+
/// </summary>
25+
public bool ReturnValue { get; }
26+
}
27+
}
28+
29+
#endif

Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/DictionarySlim{TKey,TValue}.cs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Collections;
1212
using System.Collections.Generic;
1313
using System.Diagnostics;
14+
using System.Diagnostics.CodeAnalysis;
1415
using System.Diagnostics.Contracts;
1516
using System.Runtime.CompilerServices;
1617

@@ -158,7 +159,7 @@ public bool ContainsKey(TKey key)
158159
/// <param name="key">The key to look for.</param>
159160
/// <param name="value">The value found, otherwise <see langword="default"/>.</param>
160161
/// <returns>Whether or not the key was present.</returns>
161-
public bool TryGetValue(TKey key, out TValue? value)
162+
public bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value)
162163
{
163164
Entry[] entries = this.entries;
164165

@@ -181,17 +182,6 @@ public bool TryGetValue(TKey key, out TValue? value)
181182

182183
/// <inheritdoc/>
183184
public bool TryRemove(TKey key)
184-
{
185-
return TryRemove(key, out _);
186-
}
187-
188-
/// <summary>
189-
/// Tries to remove a value with a specified key, if present.
190-
/// </summary>
191-
/// <param name="key">The key of the value to remove.</param>
192-
/// <param name="result">The removed value, if it was present.</param>
193-
/// <returns>Whether or not the key was present.</returns>
194-
public bool TryRemove(TKey key, out TValue? result)
195185
{
196186
Entry[] entries = this.entries;
197187
int bucketIndex = key.GetHashCode() & (this.buckets.Length - 1);
@@ -219,17 +209,13 @@ public bool TryRemove(TKey key, out TValue? result)
219209
this.freeList = entryIndex;
220210
this.count--;
221211

222-
result = candidate.Value;
223-
224212
return true;
225213
}
226214

227215
lastIndex = entryIndex;
228216
entryIndex = candidate.Next;
229217
}
230218

231-
result = null;
232-
233219
return false;
234220
}
235221

Microsoft.Toolkit.Mvvm/Messaging/StrongReferenceMessenger.cs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Buffers;
77
using System.Collections.Generic;
8+
using System.Diagnostics.CodeAnalysis;
89
using System.Runtime.CompilerServices;
910
using System.Threading;
1011
using Microsoft.Collections.Extensions;
@@ -102,7 +103,7 @@ public bool IsRegistered<TMessage, TToken>(object recipient, TToken token)
102103

103104
Recipient key = new(recipient);
104105

105-
return mapping!.ContainsKey(key);
106+
return mapping.ContainsKey(key);
106107
}
107108
}
108109

@@ -155,7 +156,7 @@ public void UnregisterAll(object recipient)
155156
}
156157

157158
// Removes all the lists of registered handlers for the recipient
158-
foreach (IMapping mapping in set!)
159+
foreach (IMapping mapping in set)
159160
{
160161
if (mapping.TryRemove(key) &&
161162
mapping.Count == 0)
@@ -168,12 +169,12 @@ public void UnregisterAll(object recipient)
168169
// dictionary (a hashed collection) only costs O(1) in the best case, while
169170
// if we had tried to iterate the whole dictionary every time we would have
170171
// paid an O(n) minimum cost for each single remove operation.
171-
this.typesMap.TryRemove(mapping.TypeArguments, out _);
172+
this.typesMap.TryRemove(mapping.TypeArguments);
172173
}
173174
}
174175

175176
// Remove the associated set in the recipients map
176-
this.recipientsMap.TryRemove(key, out _);
177+
this.recipientsMap.TryRemove(key);
177178
}
178179
}
179180

@@ -212,7 +213,7 @@ public void UnregisterAll<TToken>(object recipient, TToken token)
212213
// the opportunities to reuse existing buffers for both. When we need to reference an item
213214
// stored in the buffer with the type we know it will have, we use Unsafe.As<T> to avoid the
214215
// expensive type check in the cast, since we already know the assignment will be valid.
215-
maps = ArrayPool<object>.Shared.Rent(set!.Count);
216+
maps = ArrayPool<object>.Shared.Rent(set.Count);
216217

217218
foreach (IMapping item in set)
218219
{
@@ -256,7 +257,7 @@ public void UnregisterAll<TToken>(object recipient, TToken token)
256257
set.Remove(Unsafe.As<IMapping>(map)) &&
257258
set.Count == 0)
258259
{
259-
this.recipientsMap.TryRemove(key, out _);
260+
this.recipientsMap.TryRemove(key);
260261
}
261262
}
262263
}
@@ -297,28 +298,28 @@ public void Unregister<TMessage, TToken>(object recipient, TToken token)
297298

298299
Recipient key = new(recipient);
299300

300-
if (!mapping!.TryGetValue(key, out DictionarySlim<TToken, object>? dictionary))
301+
if (!mapping.TryGetValue(key, out DictionarySlim<TToken, object>? dictionary))
301302
{
302303
return;
303304
}
304305

305306
// Remove the target handler
306-
if (dictionary!.TryRemove(token, out _) &&
307+
if (dictionary.TryRemove(token) &&
307308
dictionary.Count == 0)
308309
{
309310
// If the map is empty, it means that the current recipient has no remaining
310311
// registered handlers for the current <TMessage, TToken> combination, regardless,
311312
// of the specific token value (ie. the channel used to receive messages of that type).
312313
// We can remove the map entirely from this container, and remove the link to the map itself
313314
// to the current mapping between existing registered recipients (or entire recipients too).
314-
mapping.TryRemove(key, out _);
315+
mapping.TryRemove(key);
315316

316317
HashSet<IMapping> set = this.recipientsMap[key];
317318

318319
if (set.Remove(mapping) &&
319320
set.Count == 0)
320321
{
321-
this.recipientsMap.TryRemove(key, out _);
322+
this.recipientsMap.TryRemove(key);
322323
}
323324
}
324325
}
@@ -379,7 +380,7 @@ public TMessage Send<TMessage, TToken>(TMessage message, TToken token)
379380
// which will always be greater or equal than the ones matching the previous test.
380381
// We're still using a checked span accesses here though to make sure an out of
381382
// bounds write can never happen even if an error was present in the logic above.
382-
pairs[2 * i] = handler!;
383+
pairs[2 * i] = handler;
383384
pairs[(2 * i) + 1] = mappingEnumerator.Key.Target;
384385
i++;
385386
}
@@ -439,7 +440,7 @@ public void Reset()
439440
/// <param name="mapping">The resulting <see cref="Mapping{TMessage,TToken}"/> instance, if found.</param>
440441
/// <returns>Whether or not the required <see cref="Mapping{TMessage,TToken}"/> instance was found.</returns>
441442
[MethodImpl(MethodImplOptions.AggressiveInlining)]
442-
private bool TryGetMapping<TMessage, TToken>(out Mapping<TMessage, TToken>? mapping)
443+
private bool TryGetMapping<TMessage, TToken>([NotNullWhen(true)] out Mapping<TMessage, TToken>? mapping)
443444
where TMessage : class
444445
where TToken : IEquatable<TToken>
445446
{

Microsoft.Toolkit.Mvvm/Messaging/WeakReferenceMessenger.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public bool IsRegistered<TMessage, TToken>(object recipient, TToken token)
6464
// of token and message types. If it exists, check if there is a matching token.
6565
return
6666
this.recipientsMap.TryGetValue(type2, out RecipientsTable? table) &&
67-
table!.TryGetValue(recipient, out IDictionarySlim? mapping) &&
67+
table.TryGetValue(recipient, out IDictionarySlim? mapping) &&
6868
Unsafe.As<DictionarySlim<TToken, object>>(mapping)!.ContainsKey(token);
6969
}
7070
}
@@ -133,7 +133,7 @@ public void UnregisterAll<TToken>(object recipient, TToken token)
133133
if (enumerator.Key.TToken == typeof(TToken) &&
134134
enumerator.Value.TryGetValue(recipient, out IDictionarySlim? mapping))
135135
{
136-
Unsafe.As<DictionarySlim<TToken, object>>(mapping)!.TryRemove(token, out _);
136+
Unsafe.As<DictionarySlim<TToken, object>>(mapping)!.TryRemove(token);
137137
}
138138
}
139139
}
@@ -151,9 +151,9 @@ public void Unregister<TMessage, TToken>(object recipient, TToken token)
151151
// Get the target mapping table for the combination of message and token types,
152152
// and remove the handler with a matching token (the entire map), if present.
153153
if (this.recipientsMap.TryGetValue(type2, out RecipientsTable? value) &&
154-
value!.TryGetValue(recipient, out IDictionarySlim? mapping))
154+
value.TryGetValue(recipient, out IDictionarySlim? mapping))
155155
{
156-
Unsafe.As<DictionarySlim<TToken, object>>(mapping)!.TryRemove(token, out _);
156+
Unsafe.As<DictionarySlim<TToken, object>>(mapping)!.TryRemove(token);
157157
}
158158
}
159159
}
@@ -184,13 +184,13 @@ public TMessage Send<TMessage, TToken>(TMessage message, TToken token)
184184
// to enumerate all the existing recipients for the token and message types pair
185185
// corresponding to the generic arguments for this invocation, and then track the
186186
// handlers with a matching token, and their corresponding recipients.
187-
foreach (KeyValuePair<object, IDictionarySlim> pair in table!)
187+
foreach (KeyValuePair<object, IDictionarySlim> pair in table)
188188
{
189189
var map = Unsafe.As<DictionarySlim<TToken, object>>(pair.Value);
190190

191191
if (map.TryGetValue(token, out object? handler))
192192
{
193-
bufferWriter.Add(handler!);
193+
bufferWriter.Add(handler);
194194
bufferWriter.Add(pair.Key);
195195
i++;
196196
}
@@ -268,7 +268,7 @@ public void Cleanup()
268268
// Remove all the mappings with no handlers left
269269
foreach (Type2 key in type2s.Span)
270270
{
271-
this.recipientsMap.TryRemove(key, out _);
271+
this.recipientsMap.TryRemove(key);
272272
}
273273
}
274274
}

0 commit comments

Comments
 (0)