带有私有setter的属性与get-only-property的对比

本文关键字:get-only-property 属性 setter | 更新日期: 2023-09-27 18:07:17

c# 6.0引入了只定义get属性的能力:

public ICommand AddCommand { get; }

现在当定义另一个属性时,ReSharper建议Auto-property可以设置为get-only:

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

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

public ICommand AddCommand { get; private set; }

公共的只获取属性(如第一个AddCommand)、私有的只获取属性(如Screenshots)和公共的私有setter属性(如第二个AddCommand)之间有什么区别?

我的WPF应用程序似乎并不关心它的公共属性(uiccommand)是否包含私有setter或根本没有setter,但肯定有区别吗?

带有私有setter的属性与get-only-property的对比

简短回答:

public ICommand AddCommand { get; }

将由readonly字段支持,并且除了执行构造函数之外,没有c#代码能够更改它。

而且,编译器将生成代码来直接分配后备文件,因为没有属性访问器。

另一方面:
public ICommand AddCommand { get; private set; }

将由一个非readonly字段支持,并且可以在任何时候由任何可以访问private成员的代码分配。

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

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

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

在其他情况下,例如,有一个通过构造函数注入服务的类,并且你想公开它(无论出于什么原因),使用只读属性是很重要的。

例如:

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,然后如果你没有null检查和访问它,你会得到一个NullReferenceException,如果不处理你的应用程序崩溃。

回到你的例子:在ICommand的情况下…你通常不访问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的风险。

希望这能澄清问题。

编译器为你做了功课后,你的属性变成了:


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