Подтвердить что ты не робот

Имущество с приватным сеттером против get-only-property

С# 6.0 вводит возможность определять свойства get-only:

public ICommand AddCommand { get; }

Теперь при определении другого свойства, подобного приведенному ниже, ReSharper предлагает, что Auto-свойство может быть сделано только для получения:

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

Более того, ReSharper ничего не говорит при определении частных геттеров:

public ICommand AddCommand { get; private set; }

Какая разница между общедоступным свойством get-only (таким как первый AddCommand), частным свойством get-only (таким как свойство Screenshots) и свойством публичного частного сеттера (например, вторым AddCommand)?

Мое приложение WPF, похоже, не заботится о том, содержит ли его публичное свойство (UICommand) частный сеттер или нет настройки, но, безусловно, должна быть разница?

4b9b3361

Ответ 1

В этом конкретном случае команд привязки это не имеет большого значения.

В других случаях, то есть с классом, который получает услугу, введенную через конструктор, и вы хотите ее открыть (по какой-либо причине), важно использовать свойства только для чтения.

Например:

public class MainViewModel 
{
    public INavigationService NavigationService { get; }

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

        NavigationService = navigationService;
    }
}

При использовании этого вы гарантируете этот инвариант класса, и он гарантирует, что NavigationService никогда не будет null, поэтому вам не нужно делать нулевые проверки против NavigationService перед его использованием. Как только он покидает конструктор, он никогда не может быть изменен (ну, кроме как через отражение).

С другой стороны, если у вас

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

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

        NavigationService = navigationService;
    }
}

тогда можно написать код (по ошибке или неопытным разработчиком), который делает NavigationService = null, а затем, если у вас нет нулевой проверки и доступа к ней, вы получите NullReferenceException, а если не обработать сбой приложений.

Возвращаясь к вашему примеру: В случае ICommand... вы обычно не получаете доступа к Commands в вашей ViewModel, назначаете его только (обычно в конструкторе или при изменении содержимого модели вашего представления, например, при изменении модели просмотра для детей и вы хотите назначить его команде для свойства родительской модели viewmodel).

В случае списка:

Если вы никогда не делаете Screenshots = new List<ScreenShot>() или Screenshots = DisplayScreenshots() в своем коде и только инициализируете его в конструкторе, то действительно лучше сделать его прочитанным только по той же причине: тогда вы можете гарантировать, что Screenshots никогда не является нулевым и вам не придется писать код, например

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

или

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

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

и вместо этого всегда используйте

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

Это имеет огромное преимущество в том, что вам нужно меньше кода, ваш код более читабельный и более ремонтопригодный, поскольку вы не можете "забыть" проверку нулевого уровня и риск NullReferenceException.

Надеюсь, что это прояснилось.

Ответ 2

Короткий ответ:

public ICommand AddCommand { get; }

будет поддерживаться полем readonly, и код С# не сможет изменить его за выполнение конструкторов.

Кроме того, компилятор будет генерировать код, чтобы напрямую назначать архивацию, так как отсутствует свойство accessor.

С другой стороны:

public ICommand AddCommand { get; private set; }

будет поддерживаться полем readonly и может быть назначено в любой момент любым кодом с доступом к закрытым членам.

В этом случае компилятор будет генерировать нормальный код установки свойств.

Для внешнего мира частный сеттер выглядит так, как будто его нет. Таким образом, это так же, как если бы оно действительно не существовало.

Ответ 3

Здесь ваши свойства после компилятора сделают домашнее задание для вас:


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; } 
}

Короче говоря, общедоступное свойство get-only может быть назначено только в конструкторе (потому что поле доступно только для чтения) или этим новым синтаксисом:

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

но как и для любого другого поля только для чтения, этот код в любом случае помещается в конструктор, поэтому нет большой разницы:

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