Skip to content

Commit 4f148cf

Browse files
authored
Merge pull request #131 from CommunityToolkit/dev/remove-can-execute-checks
Remove CanExecute checks from Execute logic
2 parents e1ec425 + 3daad89 commit 4f148cf

9 files changed

+148
-100
lines changed

CommunityToolkit.Mvvm/Input/AsyncRelayCommand.cs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -280,36 +280,31 @@ public void Execute(object? parameter)
280280
/// <inheritdoc/>
281281
public Task ExecuteAsync(object? parameter)
282282
{
283-
if (CanExecute(parameter))
284-
{
285-
Task executionTask;
283+
Task executionTask;
286284

287-
if (this.execute is not null)
288-
{
289-
// Non cancelable command delegate
290-
executionTask = ExecutionTask = this.execute();
291-
}
292-
else
293-
{
294-
// Cancel the previous operation, if one is pending
295-
this.cancellationTokenSource?.Cancel();
296-
297-
CancellationTokenSource cancellationTokenSource = this.cancellationTokenSource = new();
285+
if (this.execute is not null)
286+
{
287+
// Non cancelable command delegate
288+
executionTask = ExecutionTask = this.execute();
289+
}
290+
else
291+
{
292+
// Cancel the previous operation, if one is pending
293+
this.cancellationTokenSource?.Cancel();
298294

299-
// Invoke the cancelable command delegate with a new linked token
300-
executionTask = ExecutionTask = this.cancelableExecute!(cancellationTokenSource.Token);
301-
}
295+
CancellationTokenSource cancellationTokenSource = this.cancellationTokenSource = new();
302296

303-
// If concurrent executions are disabled, notify the can execute change as well
304-
if (!this.allowConcurrentExecutions)
305-
{
306-
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
307-
}
297+
// Invoke the cancelable command delegate with a new linked token
298+
executionTask = ExecutionTask = this.cancelableExecute!(cancellationTokenSource.Token);
299+
}
308300

309-
return executionTask;
301+
// If concurrent executions are disabled, notify the can execute change as well
302+
if (!this.allowConcurrentExecutions)
303+
{
304+
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
310305
}
311306

312-
return Task.CompletedTask;
307+
return executionTask;
313308
}
314309

315310
/// <inheritdoc/>

CommunityToolkit.Mvvm/Input/AsyncRelayCommand{T}.cs

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -282,37 +282,31 @@ public void Execute(object? parameter)
282282
/// <inheritdoc/>
283283
public Task ExecuteAsync(T? parameter)
284284
{
285-
if (CanExecute(parameter))
286-
{
287-
Task executionTask;
285+
Task executionTask;
288286

289-
290-
if (this.execute is not null)
291-
{
292-
// Non cancelable command delegate
293-
executionTask = ExecutionTask = this.execute(parameter);
294-
}
295-
else
296-
{
297-
// Cancel the previous operation, if one is pending
298-
this.cancellationTokenSource?.Cancel();
299-
300-
CancellationTokenSource cancellationTokenSource = this.cancellationTokenSource = new();
287+
if (this.execute is not null)
288+
{
289+
// Non cancelable command delegate
290+
executionTask = ExecutionTask = this.execute(parameter);
291+
}
292+
else
293+
{
294+
// Cancel the previous operation, if one is pending
295+
this.cancellationTokenSource?.Cancel();
301296

302-
// Invoke the cancelable command delegate with a new linked token
303-
executionTask = ExecutionTask = this.cancelableExecute!(parameter, cancellationTokenSource.Token);
304-
}
297+
CancellationTokenSource cancellationTokenSource = this.cancellationTokenSource = new();
305298

306-
// If concurrent executions are disabled, notify the can execute change as well
307-
if (!this.allowConcurrentExecutions)
308-
{
309-
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
310-
}
299+
// Invoke the cancelable command delegate with a new linked token
300+
executionTask = ExecutionTask = this.cancelableExecute!(parameter, cancellationTokenSource.Token);
301+
}
311302

312-
return executionTask;
303+
// If concurrent executions are disabled, notify the can execute change as well
304+
if (!this.allowConcurrentExecutions)
305+
{
306+
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
313307
}
314308

315-
return Task.CompletedTask;
309+
return executionTask;
316310
}
317311

318312
/// <inheritdoc/>

CommunityToolkit.Mvvm/Input/RelayCommand.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,6 @@ public bool CanExecute(object? parameter)
7474
/// <inheritdoc/>
7575
public void Execute(object? parameter)
7676
{
77-
if (CanExecute(parameter))
78-
{
79-
this.execute();
80-
}
77+
this.execute();
8178
}
8279
}

CommunityToolkit.Mvvm/Input/RelayCommand{T}.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,7 @@ public bool CanExecute(object? parameter)
9494
[MethodImpl(MethodImplOptions.AggressiveInlining)]
9595
public void Execute(T? parameter)
9696
{
97-
if (CanExecute(parameter))
98-
{
99-
this.execute(parameter);
100-
}
97+
this.execute(parameter);
10198
}
10299

103100
/// <inheritdoc/>

tests/CommunityToolkit.Mvvm.UnitTests/Test_AsyncRelayCommand.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,13 @@ public void Test_AsyncRelayCommand_WithCanExecuteFunctionFalse()
112112

113113
command.Execute(null);
114114

115-
Assert.AreEqual(ticks, 0);
115+
// It is the caller's responsibility to ensure that CanExecute is true
116+
// before calling Execute. This check verifies the logic is still called.
117+
Assert.AreEqual(ticks, 1);
116118

117119
command.Execute(new object());
118120

119-
Assert.AreEqual(ticks, 0);
121+
Assert.AreEqual(ticks, 2);
120122
}
121123

122124
[TestMethod]
@@ -289,11 +291,6 @@ private static async Task Test_AsyncRelayCommand_AllowConcurrentExecutions_TestL
289291
Assert.IsFalse(command.CanBeCanceled);
290292
Assert.IsFalse(command.IsCancellationRequested);
291293

292-
Task newTask = command.ExecuteAsync(null);
293-
294-
// Execution failed, so a completed task was returned
295-
Assert.AreSame(newTask, Task.CompletedTask);
296-
297294
tcs.SetResult(null);
298295

299296
await task;

tests/CommunityToolkit.Mvvm.UnitTests/Test_AsyncRelayCommand{T}.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,12 @@ public void Test_AsyncRelayCommandOfT_WithCanExecuteFunctionFalse()
105105

106106
command.Execute("2");
107107

108-
Assert.AreEqual(ticks, 0);
108+
// Like in the RelayCommand test, ensure Execute is unconditionally invoked
109+
Assert.AreEqual(ticks, 2);
109110

110111
command.Execute("42");
111112

112-
Assert.AreEqual(ticks, 0);
113+
Assert.AreEqual(ticks, 42);
113114
}
114115

115116
[TestMethod]
@@ -231,11 +232,6 @@ private static async Task Test_AsyncRelayCommandOfT_AllowConcurrentExecutions_Te
231232
Assert.IsFalse(command.CanBeCanceled);
232233
Assert.IsFalse(command.IsCancellationRequested);
233234

234-
Task newTask = command.ExecuteAsync(null);
235-
236-
// Execution failed, so a completed task was returned
237-
Assert.AreSame(newTask, Task.CompletedTask);
238-
239235
tcs.SetResult(null);
240236

241237
await task;

0 commit comments

Comments
 (0)