SeToY
SeToY

Reputation: 5895

Property with private setter versus get-only-property

C# 6.0 introduces the ability to define get-only properties:

public ICommand AddCommand { get; }

Now when defining another property like the following, ReSharper suggests Auto-property can be made get-only:

private List<Screenshot> Screenshots { get; set; }

Futhermore, ReSharper doesn't say a thing when defining private getters:

public ICommand AddCommand { get; private set; }

What's the difference between a public get-only property (such as the first AddCommand), a private get-only property (such as the Screenshots property) and the public private setter property (such as the second AddCommand)?

My WPF application doesn't seem to care whether its public property (UICommand) contains a private setter or no setter at all, but surely there must be a difference?

Upvotes: 27

Views: 11570

Answers (4)

Alexander Malakhov
Alexander Malakhov

Reputation: 3529

Since C# 9 we also have init accessors. Basically it's get-only property with an exception for object initializers:

struct Point
{
    public int X { get; init; }
    public int Y { get; init; }
}
...
var p = new Point() { X = 42, Y = 13 };

See MS: Init Only Setters.

Upvotes: -1

sotonika
sotonika

Reputation: 161

Here's what your properties become after compiler did homework for you:


1. public ICommand AddCommand { get; }:

private readonly ICommand <AddCommand>k__BackingField;
public ICommand AddCommand {
    get { return this.<AddCommand>k__BackingField; }
}


2. private List<Screenshot> Screenshots { get; set; }:

private List<Screenshot> <Screenshots>k__BackingField;
private List<Screenshot> Screenshots { 
    get { return this.<Screenshots>k__BackingField; }
    set { this.<Screenshots>k__BackingField = value; } 
}


3. public ICommand AddCommand { get; private set; }:

private ICommand <AddCommand>k__BackingField;
public ICommand AddCommand { 
    get { return this.<AddCommand>k__BackingField; } 
    private set { this.<AddCommand>k__BackingField = value; } 
}

In short, public get-only property can be assigned only in constructor (because the field is read-only) or by this new syntax:

public ICommand AddCommand { get; } = new MyCommand(); 

but as for any other read-only field, this code is anyway put into constructor, so there is no big difference:

public MyClass1()
{
    this.<AddCommand>k__BackingField = new MyCommand();
}

Upvotes: 7

Tseng
Tseng

Reputation: 64141

In this specific case of binding commands, it doesn't really matter.

In other cases, i.e. having a class that gets a service injected via constructor and you want to expose it (for whatever reason), it is important to use a read-only properties.

For example:

public class MainViewModel 
{
    public INavigationService NavigationService { get; }

    public MainViewModel(INavigationService navigationService) 
    {
        if(navigationService == null)
            throw new ArgumentNullException(navigationServie);

        NavigationService = navigationService;
    }
}

When using this, you guarantee this class invariants and it is assuring that NavigationService will never be null, hence you don't need to do null checks against NavigationService before using it. Once it leaves the constructor, it can't ever be changed (well, except through reflection).

On the other side if you have

public class MainViewModel 
{
    public INavigationService NavigationService { get; private set; }

    public MainViewModel(INavigationService navigationService) 
    {
        if(navigationService == null)
            throw new ArgumentNullException(navigationServie);

        NavigationService = navigationService;
    }
}

then it's possible to write code (by mistake or by a unexperienced developer) which does NavigationService = null and then if you don't have null-checks and access it, you will get a NullReferenceException and if not handled your application crashes.

Coming back to your example: In case of ICommand... you usually don't access Commands within your ViewModel, only assign it (usually in the constructor or when your view model's content changes, like a child viewmodel changed and you want to assign it's command to the parent viewmodel command property).

In case of a list:

If you never do Screenshots = new List<ScreenShot>() or Screenshots = DisplayScreenshots() in your code and only initialize it in the constructor, then it's indeed better to make it read only for the same reason: Then you can guarantee that Screenshots is never null and you won't have to write code such as

if(Screenshots != null) 
{
    Screenshots.Add(new Screenshot(...));
}

or

if(Screenshot == null) 
{
    Screenshots = new List<Screenshot>();
}

Screenshots.Add(new Screenshot(...));

again and instead always use

Screenshots.Add(new Screenshot(...));

This has a huge advantage that you need less code, your code is more readable and more maintainable, since you can't "forget" a null-check and risk a NullReferenceException.

Hope that cleared it up.

Upvotes: 13

Paulo Morgado
Paulo Morgado

Reputation: 14846

Short answer:

public ICommand AddCommand { get; }

will be backed by a readonly field and no C# code will be able to change it beyond the execution of the constructors.

Also, the compiler will generate code to directly assign the backing filed, as there is no property accessor.

On the other hand:

public ICommand AddCommand { get; private set; }

will be backed by a non-readonly field and can be assigned at any time by any code with access to private members.

In this case, the compiler will generate normal property setting code.

To the outside world, a private setter is as if it doesn't exist. So, it's the same as if it didn't really exist.

Upvotes: 28

Related Questions