Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Don't use Delegate.Method in ServiceDescriptor #114008

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

Merged
merged 2 commits into from
Apr 9, 2025

Conversation

MichalStrehovsky
Copy link
Member

The problem with this API is that in a trimmed app, accessing Method on a delegate type forces trimming to consider all delegate targets also targets of reflection (since we're grabbing a MethodInfo for whatever is the target). We do try to optimize this and only consider targets of the same delegate type, but in this case it means Func<T, U> and Func<T,U,V> which are very popular delegate types. Avoiding Delegate.Method is a 0.8% size saving for the Microsoft Store app.

The differences are:

  • Originally this was calling ToString on the MethodInfo, which returns the name and signature of the method. I'm replacing this with FullName of the owning type and the name of the method. This is both more information and less information - we get owning type, we lose signature. It feels more useful nevertheless.
  • When the app is trimmed and StackTraceSupport feature switch is set to false, this will return question marks.

The 0.8% size savings is present no matter the value of the StackTraceSupport switch.

The problem with this API is that in a trimmed app, accessing `Method` on a delegate type forces trimming to consider all delegate targets also targets of reflection (since we're grabbing a `MethodInfo` for whatever is the target). We do try to optimize this and only consider targets of the same delegate type, but in this case it means `Func<T, U>` and `Func<T,U,V>` which are very popular delegate types. Avoiding `Delegate.Method` is a 0.8% size saving for the Microsoft Store app.

The differences are:
* Originally this was calling `ToString` on the `MethodInfo`, which returns the name and signature of the method. I'm replacing this with `FullName` of the owning type and the name of the method. This is both more information and less information - we get owning type, we lose signature. It feels more useful nevertheless.
* When the app is trimmed and `StackTraceSupport` feature switch is set to false, this will return question marks.

The 0.8% size savings is present no matter the value of the `StackTraceSupport` switch.
@Copilot Copilot AI review requested due to automatic review settings March 28, 2025 13:57
Copy link
Contributor

@Copilot Copilot AI left a 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 PR updates ServiceDescriptor’s ToString implementation for service factories to avoid using Delegate.Method, reducing trimming impact and saving app size.

  • Replaces calls to Delegate.Method.ToString with explicit formatting of the declaring type’s full name and method name.
  • Uses conditional compilation (#if NET9_0_OR_GREATER) to support newer diagnostics while retaining legacy behavior.

@@ -262,7 +263,17 @@ public override string ToString()

if (KeyedImplementationFactory != null)
{
return lifetime + $"{nameof(KeyedImplementationFactory)}: {KeyedImplementationFactory.Method}";
#if NET9_0_OR_GREATER
Copy link
Preview

Copilot AI Mar 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for retrieving and formatting method information for KeyedImplementationFactory is duplicated for ImplementationFactory. Consider refactoring this logic into a shared helper method to reduce duplication and improve maintainability.

Copilot uses AI. Check for mistakes.

@@ -276,7 +287,17 @@ public override string ToString()

if (ImplementationFactory != null)
{
return lifetime + $"{nameof(ImplementationFactory)}: {ImplementationFactory.Method}";
#if NET9_0_OR_GREATER
Copy link
Preview

Copilot AI Mar 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same method info retrieval logic is used here as for KeyedImplementationFactory. Refactoring this logic into a common helper can ensure consistency and reduce maintenance effort.

Copilot uses AI. Check for mistakes.

Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection
See info in area-owners.md if you want to be subscribed.

@steveharter
Copy link
Member

Some test failures:

    Microsoft.Extensions.DependencyInjection.ServiceCollectionDescriptorExtensionsTest.ServiceDescriptor_ToString(descriptor: ServiceType: Microsoft.Extensions.DependencyInjection.Specification.Fakes.IFakeService Lifetime: Scoped ImplementationFactory: Microsoft.Extensions.DependencyInjection.ServiceCollectionDescriptorExtensionsTest.CreateFakeService, expectedString: "ServiceType: Microsoft.Extensions.DependencyInject"···) [FAIL]
      Assert.Equal() Failure: Strings differ
                                        ↓ (pos 169)
      Expected: ···"ependencyInjection.Specification.Fakes.Fa"···
      Actual:   ···"ependencyInjection.ServiceCollectionDescr"···

@MichalStrehovsky
Copy link
Member Author

Some test failures:

I've updated the tests to expect the new format, CI looks good now.

@MichalStrehovsky MichalStrehovsky merged commit ccf8e7f into dotnet:main Apr 9, 2025
84 of 87 checks passed
@MichalStrehovsky MichalStrehovsky deleted the servicedescr branch April 9, 2025 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants