属性 with private setter 与 get-only-属性

Property with private setter versus get-only-property

C# 6.0 引入了定义 get-only 属性的功能:

public ICommand AddCommand { get; }

现在,当像下面这样定义另一个 属性 时,ReSharper 建议 Auto-属性 可以设为 get-only:

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

此外,ReSharper 在定义私有 getter 时什么也没说:

public ICommand AddCommand { get; private set; }

一个public get-only 属性(比如第一个AddCommand),一个private get-only 属性(比如Screenshots属性)和publicprivatesetter属性(比如第二个AddCommand)?

我的 WPF 应用程序似乎并不关心其 public 属性 (UICommand) 是否包含私有 setter 或根本不包含 setter,但肯定存在一定有区别吗?

在这种绑定命令的特定情况下,这并不重要。

在其他情况下,即有一个 class 通过构造函数注入服务并且您想公开它(无论出于何种原因),使用只读属性很重要。

例如:

public class MainViewModel 
{
    public INavigationService NavigationService { get; }

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

        NavigationService = navigationService;
    }
}

当使用它时,你保证了这个 class 不变量,它确保 NavigationService 永远不会是 null,因此你不需要对 [= 进行空检查15=] 在使用它之前。一旦它离开了构造函数,就永远无法更改(好吧,除了通过反射)。

在另一边,如果你有

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 的情况下......你通常不会在你的 ViewModel 中访问命令,只分配它(通常在构造函数中或当你的视图模型的内容发生变化时,比如子视图模型已更改,您希望将其命令分配给父视图模型命令 属性).

如果是列表:

如果您从不在代码中执行 Screenshots = new List<ScreenShot>()Screenshots = DisplayScreenshots() 并且仅在构造函数中对其进行初始化,那么出于同样的原因将其设置为只读确实更好:那么您可以保证Screenshots 永远不会为 null,您不必编写

这样的代码
if(Screenshots != null) 
{
    Screenshots.Add(new Screenshot(...));
}

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

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

再次使用

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

这有一个巨大的优势,您需要更少的代码,您的代码更易读且更易于维护,因为您不能 "forget" 空检查并冒 NullReferenceException 的风险。

希望一切顺利。

简答:

public ICommand AddCommand { get; }

将由 readonly 字段支持,并且除了构造函数的执行之外,任何 C# 代码都无法更改它。

此外,编译器将生成直接分配后备字段的代码,因为没有 属性 访问器。

另一方面:

public ICommand AddCommand { get; private set; }

将由非 readonly 字段支持,并且可以通过任何代码随时分配给私有成员。

在这种情况下,编译器将生成正常的属性设置代码。

对于外界来说,私有setter就像不存在一样。所以,就跟不存在一样。

这是编译器为您完成作业后的属性:


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

简而言之,public get-only 属性只能在构造函数中赋值(因为该字段是只读的)或者通过这个new语法:

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

但至于其他任何只读字段,这段代码无论如何都放在构造函数中,所以没有太大区别:

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