https://github.com/thecodetraveler/icommandattribute_nullableparameter_repro
A reproduction sample for CommunityToolkit.Command
https://github.com/thecodetraveler/icommandattribute_nullableparameter_repro
Last synced: about 1 year ago
JSON representation
A reproduction sample for CommunityToolkit.Command
- Host: GitHub
- URL: https://github.com/thecodetraveler/icommandattribute_nullableparameter_repro
- Owner: TheCodeTraveler
- Created: 2022-05-28T20:34:07.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2022-05-28T21:12:07.000Z (almost 4 years ago)
- Last Synced: 2025-02-14T05:31:21.726Z (about 1 year ago)
- Language: C#
- Size: 19.5 KB
- Stars: 1
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# ICommandAttribute_NullableParameter_Repro
In CommunityToolkit.Mvvm, when a tuple containing nullable reference types is used as a parameter for a method containing the attribute `[ICommand]`, the created `IAsyncRelayCommand` removes the nullability from each object.
```cs
[ICommand]
void M1((string? text, bool isBusy) parameter)
{
}
```
generates
```cs
public IRelayCommand<(string text, bool isBusy)> { get; }
// Incorrectly omitted the nullable string
// Should be `IRelayCommand<(string? text, bool isBusy)>`
```
## Reproduction Steps
1. Download/clone the following repo: https://github.com/brminnick/ICommandAttribute_NullableParameter_Repro
2. In Visual Studio, open `ICommandAttribute_NullableParameter_Repro.sln`
3. In Visual Studio, Build/Run the solution
## Reproduction Sample
The following code is also available in the following repo: https://github.com/brminnick/ICommandAttribute_NullableParameter_Repro
### ViewModel
```cs
partial class ViewModel
{
/*
* // Auto-Generates The Following Code with the following bugs:
* `string message` should be `string? messge`
* `System.Collections.Generic.List stringList` should be `System.Collections.Generic.List? stringList`
*
* partial class ViewModel
{
/// The backing field for .
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator", "8.0.0.0")]
private global::CommunityToolkit.Mvvm.Input.RelayCommand<(global::System.DateTime? date, string message, bool? shouldPrint, global::System.Collections.Generic.List stringList)>? m1Command;
/// Gets an instance wrapping .
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator", "8.0.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public global::CommunityToolkit.Mvvm.Input.IRelayCommand<(global::System.DateTime? date, string message, bool? shouldPrint, global::System.Collections.Generic.List stringList)> M1Command => m1Command ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand<(global::System.DateTime? date, string message, bool? shouldPrint, global::System.Collections.Generic.List stringList)>(new global::System.Action<(global::System.DateTime? date, string message, bool? shouldPrint, global::System.Collections.Generic.List stringList)>(M1));
}
*/
[CommunityToolkit.Mvvm.Input.ICommand]
void M1((DateTime? date, string? message, bool? shouldPrint, List? stringList) parameter)
{
var (date, message, shouldPrint, stringList) = parameter;
if (shouldPrint is not false)
{
Console.WriteLine(message + date.ToString());
if (stringList is not null)
{
foreach (var text in stringList)
Console.WriteLine(text);
}
}
}
/*
* // Auto-Generates The Following Code with the following bugs:
* `string message` should be `string? messge`
* `System.Collections.Generic.List stringList` should be `System.Collections.Generic.List? stringList`
*
* partial class ViewModel
{
/// The backing field for .
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator", "8.0.0.0")]
private global::CommunityToolkit.Mvvm.Input.AsyncRelayCommand<(global::System.DateTime? date, string message, bool? shouldPrint, global::System.Collections.Generic.List stringList)>? m2Command;
/// Gets an instance wrapping .
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator", "8.0.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand<(global::System.DateTime? date, string message, bool? shouldPrint, global::System.Collections.Generic.List stringList)> M2Command => m2Command ??= new global::CommunityToolkit.Mvvm.Input.AsyncRelayCommand<(global::System.DateTime? date, string message, bool? shouldPrint, global::System.Collections.Generic.List stringList)>(new global::System.Func<(global::System.DateTime? date, string message, bool? shouldPrint, global::System.Collections.Generic.List stringList), global::System.Threading.Tasks.Task>(M2));
}
*/
[CommunityToolkit.Mvvm.Input.ICommand]
Task M2((DateTime? date, string? message, bool? shouldPrint, List? stringList) parameter)
{
var (date, message, shouldPrint, stringList) = parameter;
if (shouldPrint is not false)
{
Console.WriteLine(message + date.ToString());
if (stringList is not null)
{
foreach (var text in stringList)
Console.WriteLine(text);
}
}
return Task.CompletedTask;
}
}
```
```cs
class View
{
public View()
{
var stringList = new List { "Hello", "World" };
var viewModel = new ViewModel();
// No warning/error
viewModel.M1Command.Execute((DateTime.UtcNow, "Hello World", true, stringList)); // No null values
// No warning/error
viewModel.M1Command.Execute((null, "Hello World", true, stringList)); // Pass `null` into Nullable struct
// Yes Nullable Warning/Error
viewModel.M1Command.Execute((DateTime.UtcNow, null, true, stringList)); // Pass `null` into Nullable string
// No warning/error
viewModel.M1Command.Execute((DateTime.UtcNow, "Hello World", null, stringList)); // Pass `null` into Nullable ValueType
// Yes Nullable Warning/Error
viewModel.M1Command.Execute((DateTime.UtcNow, "Hello World", true, null)); // Pass `null` into Nullable object
// No warning/error
await viewModel.M2Command.ExecuteAsync((DateTime.UtcNow, "Hello World", true, stringList)); // No null values
// No warning/error
await viewModel.M2Command.ExecuteAsync((null, "Hello World", true, stringList)); // Pass `null` into Nullable struct
// Yes Nullable Warning/Error
await viewModel.M2Command.ExecuteAsync((DateTime.UtcNow, null, true, stringList)); // Pass `null` into Nullable string
// No warning/error
await viewModel.M2Command.ExecuteAsync((DateTime.UtcNow, "Hello World", null, stringList)); // Pass `null` into Nullable ValueType
// Yes Nullable Warning/Error
await viewModel.M2Command.ExecuteAsync((DateTime.UtcNow, "Hello World", true, null)); // Pass `null` into Nullable object
}
}
```