|
| 1 | +--- |
| 2 | +title: "CA2016: Forward the CancellationToken parameter to methods that take one" |
| 3 | +ms.date: 06/18/2020 |
| 4 | +ms.topic: reference |
| 5 | +f1_keywords: |
| 6 | + - "ForwardCancellationTokenToInvocations" |
| 7 | + - "CA2016" |
| 8 | +helpviewer_keywords: |
| 9 | + - "ForwardCancellationTokenToInvocations" |
| 10 | + - "CA2016" |
| 11 | +author: carlossanlop |
| 12 | +ms.author: calope |
| 13 | +manager: jeffhand |
| 14 | +dev_langs: |
| 15 | + - CSharp |
| 16 | + - VB |
| 17 | +ms.workload: |
| 18 | + - "multiple" |
| 19 | +--- |
| 20 | +# CA2016: Forward the CancellationToken parameter to methods that take one |
| 21 | + |
| 22 | +||| |
| 23 | +|-|-| |
| 24 | +|TypeName|ForwardCancellationTokenToInvocations| |
| 25 | +|CheckId|CA2016| |
| 26 | +|Category|Microsoft.Performance| |
| 27 | +|Breaking Change|Non-breaking| |
| 28 | + |
| 29 | +## Cause |
| 30 | + |
| 31 | +This rule locates method invocations that could accept a <xref:System.Threading.CancellationToken> parameter, but are not passing any, and suggests to forward the parent method's `CancellationToken` to them. |
| 32 | + |
| 33 | +## Rule description |
| 34 | + |
| 35 | +This rule analyzes method definitions that take a `CancellationToken` as their last parameter, then analyzes all methods invoked in its body. If any of the method invocations can either accept a `CancellationToken` as the last parameter, or have an overload that takes a `CancellationToken` as the last parameter, then the rule suggests using that option instead to ensure that the cancellation notification gets propagated to all operations that can listen to it. |
| 36 | + |
| 37 | +> [!NOTE] |
| 38 | +> Rule CA2016 is available in all .NET versions where the `CancellationToken` type is available. See [CancellationToken "Applies to" section](https://docs.microsoft.com/dotnet/api/system.threading.cancellationtoken#moniker-applies-to) |
| 39 | +
|
| 40 | +## How to fix violations |
| 41 | + |
| 42 | +You can either fix them manually, or you can opt to let Visual Studio do it for you, by hovering over the light bulb that shows up next to the method invocation, and selecting the suggested change. |
| 43 | + |
| 44 | +The following example shows two suggested changes: |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | +It's safe to suppress a violation of this rule if you're not concerned about forwarding the canceled operation notification to lower method invocations. |
| 49 | +You can also explicitly pass `default` in C# (`Nothing` in Visual Basic) or <xref:System.Threading.CancellationToken.None> to suppress the rule violation. |
| 50 | + |
| 51 | +The rule can detect a variety of violations. The following examples show cases that the rule can detect: |
| 52 | + |
| 53 | +##### Example 1 |
| 54 | + |
| 55 | +The rule will suggest forwarding the `c` parameter from `MyMethod` to the `MyMethodWithDefault` invocation, because the method defines an optional token parameter: |
| 56 | + |
| 57 | +```csharp |
| 58 | +using System.Threading; |
| 59 | + |
| 60 | +namespace ConsoleApp |
| 61 | +{ |
| 62 | + public static class MyTestClass |
| 63 | + { |
| 64 | + public static void MyMethodWithDefault(CancellationToken ct = default) |
| 65 | + { |
| 66 | + } |
| 67 | + |
| 68 | + public static void MyMethod(CancellationToken c) |
| 69 | + { |
| 70 | + MyMethodWithDefault(); |
| 71 | + } |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +##### Fix |
| 77 | + |
| 78 | +Forward the `c` parameter: |
| 79 | +```csharp |
| 80 | + public static void MyMethod(CancellationToken c) |
| 81 | + { |
| 82 | + MyMethodWithDefault(c); |
| 83 | + } |
| 84 | +``` |
| 85 | + |
| 86 | +If you are not concerned about forwarding cancellation notifications to lower invocations, you can either: |
| 87 | + |
| 88 | +Explicitly pass `default`: |
| 89 | +```csharp |
| 90 | + public static void MyMethod(CancellationToken c) |
| 91 | + { |
| 92 | + MyMethodWithDefault(default); |
| 93 | + } |
| 94 | +``` |
| 95 | + |
| 96 | +Or explicitly pass `CancellationToken.None`: |
| 97 | +```csharp |
| 98 | + public static void MyMethod(CancellationToken c) |
| 99 | + { |
| 100 | + MyMethodWithDefault(CancellationToken.None); |
| 101 | + } |
| 102 | +``` |
| 103 | + |
| 104 | +##### Example 2 |
| 105 | + |
| 106 | +The rule will suggest forwarding the `c` parameter from `MyMethod` to the `MyMethodWithDefault` invocation, because the method has an overload that takes a `CancellationToken` parameter: |
| 107 | + |
| 108 | +```csharp |
| 109 | +using System.Threading; |
| 110 | + |
| 111 | +namespace ConsoleApp |
| 112 | +{ |
| 113 | + public static class MyTestClass |
| 114 | + { |
| 115 | + public static void MyMethodWithOverload() |
| 116 | + { |
| 117 | + } |
| 118 | + |
| 119 | + public static void MyMethodWithOverload(CancellationToken ct = default) |
| 120 | + { |
| 121 | + } |
| 122 | + |
| 123 | + public static void MyMethod(CancellationToken c) |
| 124 | + { |
| 125 | + MyMethodWithOverload(); |
| 126 | + } |
| 127 | + } |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +##### Fix |
| 132 | + |
| 133 | +Forward the `c` parameter: |
| 134 | +```csharp |
| 135 | + public static void MyMethod(CancellationToken c) |
| 136 | + { |
| 137 | + MyMethodWithOverload(c); |
| 138 | + } |
| 139 | +``` |
| 140 | + |
| 141 | +If you are not concerned about forwarding cancellation notifications to lower invocations, you can either: |
| 142 | + |
| 143 | +Explicitly pass `default`: |
| 144 | +```csharp |
| 145 | + public static void MyMethod(CancellationToken c) |
| 146 | + { |
| 147 | + MyMethodWithOverload(default); |
| 148 | + } |
| 149 | +``` |
| 150 | + |
| 151 | +Or explicitly pass `CancellationToken.None`: |
| 152 | +```csharp |
| 153 | + public static void MyMethod(CancellationToken c) |
| 154 | + { |
| 155 | + MyMethodWithOverload(CancellationToken.None); |
| 156 | + } |
| 157 | +``` |
| 158 | + |
| 159 | + |
| 160 | +#### No diagnosis |
| 161 | + |
| 162 | +##### Example 1 |
| 163 | + |
| 164 | +The `CancellationToken` parameter in the parent method is not in the last position: |
| 165 | + |
| 166 | +```csharp |
| 167 | +using System.Threading; |
| 168 | + |
| 169 | +namespace ConsoleApp |
| 170 | +{ |
| 171 | + public static class MyTestClass |
| 172 | + { |
| 173 | + public static void MyMethodWithDefault(CancellationToken ct = default) |
| 174 | + { |
| 175 | + } |
| 176 | + |
| 177 | + public static void MyMethod(CancellationToken c, int lastParameter) |
| 178 | + { |
| 179 | + MyMethodWithDefault(); |
| 180 | + } |
| 181 | + } |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +##### Example 2 |
| 186 | + |
| 187 | +The `CancellationToken` parameter in the default method is not in the last position: |
| 188 | + |
| 189 | +```csharp |
| 190 | +using System.Threading; |
| 191 | + |
| 192 | +namespace ConsoleApp |
| 193 | +{ |
| 194 | + public static class MyTestClass |
| 195 | + { |
| 196 | + public static void MyMethodWithDefault(CancellationToken ct = default, int lastParameter = 0) |
| 197 | + { |
| 198 | + } |
| 199 | + |
| 200 | + public static void MyMethod(CancellationToken c) |
| 201 | + { |
| 202 | + MyMethodWithDefault(); |
| 203 | + } |
| 204 | + } |
| 205 | +} |
| 206 | +``` |
| 207 | + |
| 208 | +##### Example 3 |
| 209 | + |
| 210 | +The `CancellationToken` parameter in the overload method is not in the last position: |
| 211 | + |
| 212 | +```csharp |
| 213 | +using System.Threading; |
| 214 | + |
| 215 | +namespace ConsoleApp |
| 216 | +{ |
| 217 | + public static class MyTestClass |
| 218 | + { |
| 219 | + public static void MyMethodWithOverload(int lastParameter) |
| 220 | + { |
| 221 | + } |
| 222 | + public static void MyMethodWithOverload(CancellationToken ct, int lastParameter) |
| 223 | + { |
| 224 | + } |
| 225 | + |
| 226 | + public static void MyMethod(CancellationToken c) |
| 227 | + { |
| 228 | + MyMethodWithOverload(); |
| 229 | + } |
| 230 | + } |
| 231 | +} |
| 232 | +``` |
| 233 | + |
| 234 | +##### Example 4 |
| 235 | + |
| 236 | +The parent method defines more than one `CancellationToken` parameter: |
| 237 | + |
| 238 | +```csharp |
| 239 | +using System.Threading; |
| 240 | + |
| 241 | +namespace ConsoleApp |
| 242 | +{ |
| 243 | + public static class MyTestClass |
| 244 | + { |
| 245 | + public static void MyMethodWithDefault(CancellationToken ct = default) |
| 246 | + { |
| 247 | + } |
| 248 | + |
| 249 | + public static void MyMethod(CancellationToken c1, CancellationToken c2) |
| 250 | + { |
| 251 | + MyMethodWithDefault(); |
| 252 | + } |
| 253 | + } |
| 254 | +} |
| 255 | +``` |
| 256 | + |
| 257 | +##### Example 5 |
| 258 | + |
| 259 | +The method with defaults defines more than one `CancellationToken` parameter: |
| 260 | + |
| 261 | +```csharp |
| 262 | +using System.Threading; |
| 263 | + |
| 264 | +namespace ConsoleApp |
| 265 | +{ |
| 266 | + public static class MyTestClass |
| 267 | + { |
| 268 | + public static void MyMethodWithDefault(CancellationToken c1 = default, CancellationToken c2 = default) |
| 269 | + { |
| 270 | + } |
| 271 | + |
| 272 | + public static void MyMethod(CancellationToken c) |
| 273 | + { |
| 274 | + MyMethodWithDefault(); |
| 275 | + } |
| 276 | + } |
| 277 | +} |
| 278 | +``` |
| 279 | + |
| 280 | +##### Example 6 |
| 281 | + |
| 282 | +The method overload defines more than one `CancellationToken` parameter: |
| 283 | + |
| 284 | +```csharp |
| 285 | +using System.Threading; |
| 286 | + |
| 287 | +namespace ConsoleApp |
| 288 | +{ |
| 289 | + public static class MyTestClass |
| 290 | + { |
| 291 | + public static void MyMethodWithOverload(CancellationToken c1, CancellationToken c2) |
| 292 | + { |
| 293 | + } |
| 294 | + |
| 295 | + public static void MyMethodWithOverload() |
| 296 | + { |
| 297 | + } |
| 298 | + |
| 299 | + public static void MyMethod(CancellationToken c) |
| 300 | + { |
| 301 | + MyMethodWithOverload(); |
| 302 | + } |
| 303 | + } |
| 304 | +} |
| 305 | +``` |
0 commit comments