question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Design Discussion] Standardise on `(X)GetService(typeof(X))` or `GetService(typeof(X)) as X`

See original GitHub issue

There’s currently a 50-50 split between the following pattern:

IServiceProvider provider = ...;
IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService))
if (edSvc != null)
{
    // Continue
}

and

if (provider.GetService(typeof(IWindowsFormsEditorService) is IWindowsFormsEditorService edSvc)
{
    // Continue
}

The former throws InvalidCastException if the service is invalid (e.g. returns new object()) while the latter lets this pass through.

From #760 I have a couple of comments as I think the use of as/is has numerous benefits

  • Clears up confusion as to what happens when the service is null - will this throw InvalidCastException or NullReferenceException. Actually, it’s neither - it returns null - but the as/is pattern avoids this confusion.
  • Most importantly, we can use the C# construct if (variable is Type variableAsType) which lets us made code more readable, express intent better etc.

It’s essentially my view is that it was the throwing InvalidCastException behavior is an oversight that didn’t account for an edge case. If we had a deliberate policy, then this in the case we’d use the force cast rather than the as cast across winforms, but we don’t. The intent is to get an object of that type. In each case we check for null so we understand that the provider can return an invalid service. In some cases we’ve accounted for where the provider returns a non-interface service, in others we haven’t. It’s my opinion that we should standardise on the latter.

Note that it could be considered a breaking change (throwing exception vs. not before) to use the force cast where an as cast was used previously. You could argue that things that failed/threw before would now succeed, but I believe this is not considered as breaking as the former.

/cc @weltkante @zsd4yr

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
weltkantecommented, Apr 17, 2019

In some cases we’ve accounted for where the provider returns a non-interface service, in others we haven’t.

I’ve seen as casts used inappropriately a lot, including in the .NET Framework codebase. Most prominently as followed by a member access, which will result in NRE instead of ICE. When I had the chance to talk to people who write such code it’s usually the argument “its more readable” than the forced cast when the type follows the expression. They ignore (or arent’ aware of) what the cast does and that it prodcues less debuggable exceptions.

Because there is so little awareness of “using the right cast for its side effects” vs. “using the pretty cast for readability” I’d not take the cast itself as strong evidence that it is the right thing to do.

In each case we check for null so we understand that the provider can return an invalid service.

You check for null because providing an interface is optional. Providing a wrong interface is a bug, like passing the wrong argument, which you usually want to find via exceptions. Debugging a first chance exception is much easier than debugging why something doesn’t happen. It’s not a mistake made often so its probably not worth throwing explicit exceptions for it. Anyways, the type you request represents a contract and you ask the service providier if he implements that contract. You go through the interface and don’t cast the service provider itself to allow him to implement the contract on another object.

Unfortunately this interface was defined before generics existed, otherwise this would have been T GetService<T>() to not allow this ambiguity.

There is precedence in ASP.NET Core (link to doc and src) to use the throwing variant, by providing the generic method as an extension method and directly implementing the force cast. You could reference Microsoft.Extensions.DependencyInjection.Abstractions.dll or implement an internal version of that extension to avoid having to do the cast on every call site.

        public static T GetService<T>(this IServiceProvider provider)
        {
            if (provider == null)
            {
                throw new ArgumentNullException(nameof(provider));
            }

            return (T)provider.GetService(typeof(T));
        }
0reactions
RussKiecommented, Jul 9, 2019

I think we’re maybe talking about two differnet things.

  1. a requested service is unavailabe (i.e… “…shut down, project closing…”) - in this case, return null and don’t throw
  2. an invalid cast (MyType)GetService(OtherType) - this should be a bug and we should fail.
Read more comments on GitHub >

github_iconTop Results From Across the Web

The difference between GetService() and ...
This post looks at the GetService() and GetRequiredService() methods of the ASP.NET Core DI container, the difference between them and which ...
Read more >
Mocking ASP Net Core GetService<T>
With this, MOQ framework throws exception: Object of type 'System.RuntimeType' cannot be converted to type UserType. Things I have tried:
Read more >
Future Milestone
[Design Discussion] Standardise on (X)GetService(typeof(X)) or GetService(typeof(X)) as X design-discussion Ongoing discussion about design without ...
Read more >
Help in Mocking ASP Net Core GetService<T> · Issue #1236
With this, MOQ framework throws exception : Object of type 'System.RuntimeType' cannot be converted to type UserType. Things i have tried:
Read more >
ServiceContainer Class (System.ComponentModel.Design)
The program provides a user interface that allows you to see the availability of services within a chain of linked services, and uses...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found