@@ -9,33 +9,67 @@ namespace StackExchange.Redis
9
9
public readonly struct RedisChannel : IEquatable < RedisChannel >
10
10
{
11
11
internal readonly byte [ ] ? Value ;
12
- internal readonly bool IsPatternBased ;
12
+ internal readonly bool _isPatternBased ;
13
13
14
14
/// <summary>
15
15
/// Indicates whether the channel-name is either null or a zero-length value.
16
16
/// </summary>
17
17
public bool IsNullOrEmpty => Value == null || Value . Length == 0 ;
18
18
19
+ /// <summary>
20
+ /// Indicates whether this channel represents a wildcard pattern (see <c>PSUBSCRIBE</c>)
21
+ /// </summary>
22
+ public bool IsPatternBased => _isPatternBased ;
23
+
19
24
internal bool IsNull => Value == null ;
20
25
26
+
27
+ /// <summary>
28
+ /// Indicates whether channels should use <see cref="PatternMode.Auto"/> when no <see cref="PatternMode"/>
29
+ /// is specified; this is enabled by default, but can be disabled to avoid unexpected wildcard scenarios.
30
+ /// </summary>
31
+ public static bool UseImplicitAutoPattern
32
+ {
33
+ get => s_DefaultPatternMode == PatternMode . Auto ;
34
+ set => s_DefaultPatternMode = value ? PatternMode . Auto : PatternMode . Literal ;
35
+ }
36
+ private static PatternMode s_DefaultPatternMode = PatternMode . Auto ;
37
+
38
+ /// <summary>
39
+ /// Creates a new <see cref="RedisChannel"/> that does not act as a wildcard subscription
40
+ /// </summary>
41
+ public static RedisChannel Literal ( string value ) => new RedisChannel ( value , PatternMode . Literal ) ;
42
+ /// <summary>
43
+ /// Creates a new <see cref="RedisChannel"/> that does not act as a wildcard subscription
44
+ /// </summary>
45
+ public static RedisChannel Literal ( byte [ ] value ) => new RedisChannel ( value , PatternMode . Literal ) ;
46
+ /// <summary>
47
+ /// Creates a new <see cref="RedisChannel"/> that acts as a wildcard subscription
48
+ /// </summary>
49
+ public static RedisChannel Pattern ( string value ) => new RedisChannel ( value , PatternMode . Pattern ) ;
50
+ /// <summary>
51
+ /// Creates a new <see cref="RedisChannel"/> that acts as a wildcard subscription
52
+ /// </summary>
53
+ public static RedisChannel Pattern ( byte [ ] value ) => new RedisChannel ( value , PatternMode . Pattern ) ;
54
+
21
55
/// <summary>
22
56
/// Create a new redis channel from a buffer, explicitly controlling the pattern mode.
23
57
/// </summary>
24
58
/// <param name="value">The name of the channel to create.</param>
25
59
/// <param name="mode">The mode for name matching.</param>
26
- public RedisChannel ( byte [ ] ? value , PatternMode mode ) : this ( value , DeterminePatternBased ( value , mode ) ) { }
60
+ public RedisChannel ( byte [ ] ? value , PatternMode mode ) : this ( value , DeterminePatternBased ( value , mode ) ) { }
27
61
28
62
/// <summary>
29
63
/// Create a new redis channel from a string, explicitly controlling the pattern mode.
30
64
/// </summary>
31
65
/// <param name="value">The string name of the channel to create.</param>
32
66
/// <param name="mode">The mode for name matching.</param>
33
- public RedisChannel ( string value , PatternMode mode ) : this ( value == null ? null : Encoding . UTF8 . GetBytes ( value ) , mode ) { }
67
+ public RedisChannel ( string value , PatternMode mode ) : this ( value == null ? null : Encoding . UTF8 . GetBytes ( value ) , mode ) { }
34
68
35
69
private RedisChannel ( byte [ ] ? value , bool isPatternBased )
36
70
{
37
71
Value = value ;
38
- IsPatternBased = isPatternBased ;
72
+ _isPatternBased = isPatternBased ;
39
73
}
40
74
41
75
private static bool DeterminePatternBased ( byte [ ] ? value , PatternMode mode ) => mode switch
@@ -87,7 +121,7 @@ private RedisChannel(byte[]? value, bool isPatternBased)
87
121
/// <param name="x">The first <see cref="RedisChannel"/> to compare.</param>
88
122
/// <param name="y">The second <see cref="RedisChannel"/> to compare.</param>
89
123
public static bool operator == ( RedisChannel x , RedisChannel y ) =>
90
- x . IsPatternBased == y . IsPatternBased && RedisValue . Equals ( x . Value , y . Value ) ;
124
+ x . _isPatternBased == y . _isPatternBased && RedisValue . Equals ( x . Value , y . Value ) ;
91
125
92
126
/// <summary>
93
127
/// Indicate whether two channel names are equal.
@@ -135,10 +169,10 @@ private RedisChannel(byte[]? value, bool isPatternBased)
135
169
/// Indicate whether two channel names are equal.
136
170
/// </summary>
137
171
/// <param name="other">The <see cref="RedisChannel"/> to compare to.</param>
138
- public bool Equals ( RedisChannel other ) => IsPatternBased == other . IsPatternBased && RedisValue . Equals ( Value , other . Value ) ;
172
+ public bool Equals ( RedisChannel other ) => _isPatternBased == other . _isPatternBased && RedisValue . Equals ( Value , other . Value ) ;
139
173
140
174
/// <inheritdoc/>
141
- public override int GetHashCode ( ) => RedisValue . GetHashCode ( Value ) + ( IsPatternBased ? 1 : 0 ) ;
175
+ public override int GetHashCode ( ) => RedisValue . GetHashCode ( Value ) + ( _isPatternBased ? 1 : 0 ) ;
142
176
143
177
/// <summary>
144
178
/// Obtains a string representation of the channel name.
@@ -159,7 +193,16 @@ internal void AssertNotNull()
159
193
if ( IsNull ) throw new ArgumentException ( "A null key is not valid in this context" ) ;
160
194
}
161
195
162
- internal RedisChannel Clone ( ) => ( byte [ ] ? ) Value ? . Clone ( ) ?? default ;
196
+ internal RedisChannel Clone ( )
197
+ {
198
+ if ( Value is null || Value . Length == 0 )
199
+ {
200
+ // no need to duplicate anything
201
+ return this ;
202
+ }
203
+ var copy = ( byte [ ] ) Value . Clone ( ) ; // defensive array copy
204
+ return new RedisChannel ( copy , _isPatternBased ) ;
205
+ }
163
206
164
207
/// <summary>
165
208
/// The matching pattern for this channel.
@@ -184,33 +227,35 @@ public enum PatternMode
184
227
/// Create a channel name from a <see cref="string"/>.
185
228
/// </summary>
186
229
/// <param name="key">The string to get a channel from.</param>
230
+ [ Obsolete ( "It is preferable to explicitly specify a " + nameof ( PatternMode ) + ", or use the " + nameof ( Literal ) + "/" + nameof ( Pattern ) + " methods" , error : false ) ]
187
231
public static implicit operator RedisChannel ( string key )
188
232
{
189
233
if ( key == null ) return default ;
190
- return new RedisChannel ( Encoding . UTF8 . GetBytes ( key ) , PatternMode . Auto ) ;
234
+ return new RedisChannel ( Encoding . UTF8 . GetBytes ( key ) , s_DefaultPatternMode ) ;
191
235
}
192
236
193
237
/// <summary>
194
238
/// Create a channel name from a <see cref="T:byte[]"/>.
195
239
/// </summary>
196
240
/// <param name="key">The byte array to get a channel from.</param>
241
+ [ Obsolete ( "It is preferable to explicitly specify a " + nameof ( PatternMode ) + ", or use the " + nameof ( Literal ) + "/" + nameof ( Pattern ) + " methods" , error : false ) ]
197
242
public static implicit operator RedisChannel ( byte [ ] ? key )
198
243
{
199
244
if ( key == null ) return default ;
200
- return new RedisChannel ( key , PatternMode . Auto ) ;
245
+ return new RedisChannel ( key , s_DefaultPatternMode ) ;
201
246
}
202
247
203
248
/// <summary>
204
249
/// Obtain the channel name as a <see cref="T:byte[]"/>.
205
250
/// </summary>
206
251
/// <param name="key">The channel to get a byte[] from.</param>
207
- public static implicit operator byte [ ] ? ( RedisChannel key ) => key . Value ;
252
+ public static implicit operator byte [ ] ? ( RedisChannel key ) => key . Value ;
208
253
209
254
/// <summary>
210
255
/// Obtain the channel name as a <see cref="string"/>.
211
256
/// </summary>
212
257
/// <param name="key">The channel to get a string from.</param>
213
- public static implicit operator string ? ( RedisChannel key )
258
+ public static implicit operator string ? ( RedisChannel key )
214
259
{
215
260
var arr = key . Value ;
216
261
if ( arr == null )
@@ -226,5 +271,15 @@ public static implicit operator RedisChannel(byte[]? key)
226
271
return BitConverter . ToString ( arr ) ;
227
272
}
228
273
}
274
+
275
+ #if DEBUG
276
+ // these exist *purely* to ensure that we never add them later *without*
277
+ // giving due consideration to the default pattern mode (UseImplicitAutoPattern)
278
+ // (since we don't ship them, we don't need them in release)
279
+ [ Obsolete ( "Watch for " + nameof ( UseImplicitAutoPattern ) , error : true ) ]
280
+ private RedisChannel ( string value ) => throw new NotSupportedException ( ) ;
281
+ [ Obsolete ( "Watch for " + nameof ( UseImplicitAutoPattern ) , error : true ) ]
282
+ private RedisChannel ( byte [ ] ? value ) => throw new NotSupportedException ( ) ;
283
+ #endif
229
284
}
230
285
}
0 commit comments