2

I was wandering if I could initialize a (reference type) property (when its value is null) using a reference to this keyword, but without using the constructor.
In some cases I do not want to use the constructor to initialize the property so, if no one accesses it, its value will not be created.
Furthermore, I don't like to separate the property declaration from it's initialization in the constructor, if possible.

A typical example is a Command declaration for MVVM pattern programming:

private Command FAddRecordCommand = null;
public Command AddRecordCommand
{
    get
    {
        if (this.FAddRecordCommand == null)
        {
            this.FAddRecordCommand = new Command(this.AddRecordCommandExecute);
        }
        return this.FAddRecordCommand;
    }
}

private async void AddRecordCommandExecute()
{
    //do something
}

I don't like to write three times the name of the FAddRecordCommand member...

I tried with Auto-implemented properties, but the this keyword is not accessible in the initialization:

public Command AddRecordCommand { get; } = new Command(this.AddRecordCommandExecute);

The compiler throws the error: Keyword 'this' is not available in current context

Is there a way to use the one-line declaration like the Auto-Implemented property provides, but making access to this?

Formentz
  • 1,083
  • 1
  • 14
  • 20

3 Answers3

3

This can be done using the null-coalescing assignment operator:

private Command addRecordCommand = null;
public Command AddRecordCommand
    => addRecordCommand ??= new Command(AddRecordCommandExecute);

This assigns to addRecordCommand only if it is null, then returns the value of that field.

Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35
  • I didn't know of null-coalescing assignment operator, but, anyway, it is available only for C# 8.0 and later, I am using C# 7.3. Anyway it's good to know, thanks! – Formentz Dec 17 '21 at 16:27
  • For C#<8 there's always the slightly more verbose version: `addRecordCommand = addRecordCommand ?? new Command(AddRecordCommandExecute);` – Johnathan Barclay Dec 17 '21 at 16:34
  • but this way it continues to assign the same value to addRecordCommand member every time I access the property, isn't it? I am not saying it creates a new value, but assigns itself the same value after the first initialization – Formentz Dec 17 '21 at 16:38
  • No, it will be assigned only once. See [this example](https://dotnetfiddle.net/YXV21a). – Johnathan Barclay Dec 17 '21 at 16:41
  • I changed the fiddler example: https://dotnetfiddle.net/WZamkS you can see that the private member continues to be assigned every time the public property value is read – Formentz Dec 17 '21 at 16:52
  • @Formentz It isn't created every time. Look at this example - when you run it, note that the "ClassWithConstructor has been created" message is displayed only once, even though the property is accessed 3 times: https://dotnetfiddle.net/PvGtEf – Matthew Watson Dec 17 '21 at 17:03
  • @Mattew Watson: with the null coalescing assignment operator it is all right, it works as expected. I was arguing that the second trick proposed by Jonathan in the comments is going to assign the same reference of the same object instance to the private member every time you read the property. I mean this: `addRecordCommand = addRecordCommand ?? new Command(AddRecordCommandExecute);` – Formentz Dec 20 '21 at 08:36
3

It seems that your are looking for lazy initialization which you can implement with a help of LazyInitializer

// Eager command creation
private Command AddRecordCommandExecute() {
  //TODO: put the right creation and initialization code here
  return new Command(this.AddRecordCommandExecute);
}

// Backing Field
private Command FAddRecordCommand;

// Lazy initialized property:
// AddRecordCommandExecute() will be run once 
// on the first AddRecordCommand read   
public Command AddRecordCommand => LazyInitializer
  .EnsureInitialized(ref FAddRecordCommand, AddRecordCommandExecute);
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • Note that unlike [the null-coalescing operator](https://stackoverflow.com/questions/4619593/is-the-null-coalesce-operator-thread-safe), `Lazy` is thread safe. So for general purpose use, it's safer to use `Lazy` as per this answer rather than `??=` - especially if the object being newed is expensive or has side-effects. – Matthew Watson Dec 17 '21 at 16:26
  • seems that LazyInitializer does just like what I did with my extension method, so I can update my answer.. but is it still thread safe or not? – Formentz Dec 17 '21 at 16:45
  • 2
    @Formentz: quotation from the manual: "The methods of `LazyInitializer` are **thread-safe** and may be called from multiple threads concurrently." https://learn.microsoft.com/en-us/dotnet/api/system.threading.lazyinitializer?view=net-6.0#thread-safety – Dmitry Bychenko Dec 17 '21 at 16:47
  • @Dmitry Bychenko great! I am going to update my answer with your suggestion and replace my extension method with LazyInitializer! thank you man! – Formentz Dec 17 '21 at 16:54
0

I figured out a way to reduce the lines of code, and the access to the private member, with this technique:

private Command FAddRecordCommand = null;
public Command AddRecordCommand => LazyInitializer.EnsureInitialized(ref this.FAddRecordCommand, () => new Command(this.AddRecordCommandExecute));

private async void AddRecordCommandExecute()
{
    //do something
}

Here I used:

  • a lambda expression => to replace the get function of the property
  • the ref keyword to pass a reference to the FAddRecordCommand member to allow its value to be changed
  • a lambda function () => new ??? to return the new value needed for initialization
  • the LazyInitializer.EnsureInitialized method to assign the initialization value (thanks to Dmitry Bychenko for the suggestion)

This way the this keyword is available, the declaration requires only one line of code, and I access the private member only once.

passing the member as ref allows its value to be changed, and the Func<TProp> allows the new value to be created only when initial value is null.

If you don't like lambda expressions for property declaration, you can still declare it in one line:

public Command AddRecordCommand { get { return LazyInitializer.EnsureInitialized(ref this.FAddRecordCommand, () => new Command(this.AddRecordCommandExecute)); } }
Formentz
  • 1,083
  • 1
  • 14
  • 20
  • 1
    You re-invented [Lazy](https://learn.microsoft.com/en-us/dotnet/api/system.lazy-1) – Klaus Gütter Dec 17 '21 at 16:16
  • I think you've just reinvented [`Lazy`](https://learn.microsoft.com/en-us/dotnet/api/system.lazy-1?view=net-6.0), only in a thread-unsafe way... – Matthew Watson Dec 17 '21 at 16:17
  • @KlausGütter and Mattew Watson: you are both right, but Lazy is not going to fit my needs.. LazyInitializer instead, as suggested by Dmitry, is the solution. Thanks anyway for yor suggestions – Formentz Dec 20 '21 at 08:39