r3plica
r3plica

Reputation: 13367

null-coalescing operator on a property get

So, I am reading an article at the moment and I came across some code which has caused me to question it.

The code looks like this:

private UserService _userService = null;

protected UserService UserService
{
    get { return _userService ?? Request.GetOwinContext().GetUserManager<UserService>(); }
}

To me, it looks like the private property will always be null, so using the null-coalescing operator is useless. I would imagine it is as useful as doing this:

protected UserService UserService
{
    get { return Request.GetOwinContext().GetUserManager<UserService>(); }
}

Am I missing something? And while we are on the subject, why do this:

private UserService _userService = null;

surely that is the same as

private UserService _userService;

Please help me clarify my suspicions :D

Upvotes: 2

Views: 3441

Answers (4)

Jesper Baltzersen
Jesper Baltzersen

Reputation: 123

As @Rob mentioned in the comments it's a normal pattern doing

get { return _userService ?? _userService = Request.GetOwinContext().GetUserManager<UserService>(); }

C# 8 added the null-coalescing assignment operator which adds a little syntactic sugar to this pattern:

get { return _userService ??= Request.GetOwinContext().GetUserManager<UserService>(); }

This accomplishes the same as _userService ?? _userService =.

Upvotes: 0

Fabjan
Fabjan

Reputation: 13676

I'd add my two cents here :

One possible reason is that developer(s) wanted to create readonly property that uses value of _userService field if it was assigned by user (di container) only and if it wasn't simply return value from Identity value provider (most likely this provider uses singleton per http context).

Generally speaking, i kind of dislike the idea of assigning field's value inside of getter method of a property since setter exists for this sole purpose.

Provided that we'd like to change something in code to make it more 'in a normal way' we could set this field in a constructor and add optional parameter to it :

private UserService _userService;

protected UserService UserService
{
    get
    {
        return _userService;
    }
}

public MyController(UserService service = null)
{
   this._userService = service ?? 
          Request.GetOwinContext().GetUserManager<UserService>();
}

Now code is more testable and neat. It also allows us to use DI frameworks. The only drawback of this approach is that we moved away from lazy loading. Well, we could use it again with Lazy<T> :

private Lazy<UserService> _userService;

protected UserService UserService
{
    get
    {
        return _userService.Value;
    }
}

public MyController(UserService service = null)
{
     service = service ?? 
          Request.GetOwinContext().GetUserManager<UserService>();

     _userService = new Lazy<UserService>(service);
}

Now it is easier to unit test our code, we can use DI framework and benefit from using of lazy loading.

P.S.

Assign of null value explicitly to field of reference type doesn't make much sense to me. The only reason i could think of is that it slightly improve readability but this seem to be entirely opinion based.

Upvotes: 0

Herman Cordes
Herman Cordes

Reputation: 4976

You're right, the private property will always be null in your example.

One way to solve it, is using this code:

private UserService _userService = null;

protected UserService UserService
{
    get
    {
        return _userService ?? (_userService = Request.GetOwinContext().GetUserManager<UserService>());
    }
}

This way the private property is used when not null, or is initialized to the assigned value. When the statement after the null-coalescing operator ?? is executed, the assigned value to _userService is actually returned by the propery.

Upvotes: 6

Brian Mains
Brian Mains

Reputation: 50728

It's not uncommon to have that approach, but usually it comes with a parameterized constructor like:

public class X
{
   private UserService _userService = null;

   protected UserService UserService
   {
       get { return _userService ?? Request.GetOwinContext().GetUserManager<UserService>(); }
   }


   public X() { }

   public X(UserService svc) { _userService = svc; }

Or some other way of populating the service method, like a method:

public void SetUserService(UserService svc) { _userSErvice = svc; }

The benefit to doing that you can construct with with a mocked UserService service using a mocking framework like Moq or Rhino.Mocks.

Upvotes: 1

Related Questions