-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add contravariance to RenderFragment<TValue> #64822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Greetings! You've submitted a PR that modifies code that is shared with https://github.com/dotnet/runtime . Please make sure you synchronize this code with the changes in that repo! |
Co-authored-by: javiercn <[email protected]>
Co-authored-by: javiercn <[email protected]>
Co-authored-by: javiercn <[email protected]>
|
Tests are only for demonstration purposes. We will remove them as this is just a compiler restriction that we are relaxing |
| /// </summary> | ||
| /// <typeparam name="TValue">The type of object.</typeparam> | ||
| /// <param name="value">The value used to build the content.</param> | ||
| public delegate RenderFragment RenderFragment<TValue>(TValue value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this public API change? Unshipped file does not need an update? With *REMOVED* etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but the analyzer might not pick this up. To be clear, it's a public API change, but I don't believe this is breaking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds contravariance to the RenderFragment<TValue> delegate by marking the TValue type parameter with the in modifier. This enables passing render fragments that accept base types where derived types are expected, simplifying generic component composition and eliminating the need for complex reflection-based adapters when working with dynamic components.
Key Changes
- Modified
RenderFragment<TValue>delegate signature to makeTValuecontravariant - Added 7 comprehensive unit tests covering various contravariance scenarios (base classes, interfaces, method parameters, DynamicComponent usage)
- Tests validate both the core functionality and the specific issue scenario described in the GitHub issue
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Components/Components/src/RenderFragment.cs | Added in modifier to TValue type parameter, making the delegate contravariant |
| src/Components/Components/test/RenderFragmentContravarianceTest.cs | Added 4 tests covering contravariance with base classes, interfaces, method parameters, and object type |
| src/Components/Components/test/RenderFragmentIssueScenarioTest.cs | Added 3 tests validating the exact GitHub issue scenario with DynamicComponent and demonstrating elimination of adapter code |
| /// <typeparam name="TValue">The type of object.</typeparam> | ||
| /// <param name="value">The value used to build the content.</param> |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The XML documentation for the RenderFragment delegate should be updated to mention that TValue is contravariant. This is important for API documentation because contravariance affects how developers can use the delegate. Consider adding a remark or updating the typeparam description to note that TValue is contravariant, allowing render fragments that accept base types to be used where derived types are expected.
| /// <typeparam name="TValue">The type of object.</typeparam> | |
| /// <param name="value">The value used to build the content.</param> | |
| /// <typeparam name="TValue"> | |
| /// The type of object. This type parameter is contravariant, so a <see cref="RenderFragment{TValue}"/> | |
| /// that accepts a base type can be used where a fragment for a derived type is expected. | |
| /// </typeparam> | |
| /// <param name="value">The value used to build the content.</param> | |
| /// <remarks> | |
| /// Because <typeparamref name="TValue"/> is contravariant, you can, for example, use a | |
| /// <see cref="RenderFragment{TValue}"/> declared as <c>RenderFragment<object></c> in place of | |
| /// a <c>RenderFragment<string></c>, since <c>string</c> derives from <c>object</c>. | |
| /// </remarks> |
Add contravariance to RenderFragment
Mark TValue as contravariant in RenderFragment
Description
Marking
TValueas contravariant with theinmodifier enables passing render fragments that accept base types where derived types are expected, eliminating complex reflection-based adapters in generic component composition.Core Change:
Enables:
Testing:
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.