具有私有setter与get-only-property的属性

SeT*_*ToY 21 c# wpf mvvm c#-6.0

C#6.0引入了定义只获取属性的功能:

public ICommand AddCommand { get; }
Run Code Online (Sandbox Code Playgroud)

现在,在定义下面的另一个属性时,ReSharper建议Auto-property可以变为get-only:

private List<Screenshot> Screenshots { get; set; }
Run Code Online (Sandbox Code Playgroud)

此外,ReSharper在定义私人吸气剂时并未说出一句话:

public ICommand AddCommand { get; private set; }
Run Code Online (Sandbox Code Playgroud)

公共get-only属性(例如第一个AddCommand),私有get-only属性(例如Screenshots属性)和public private setter属性(例如第二个AddCommand)之间的区别是什么?

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

Pau*_*ado 21

简短回答:

public ICommand AddCommand { get; }
Run Code Online (Sandbox Code Playgroud)

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

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

另一方面:

public ICommand AddCommand { get; private set; }
Run Code Online (Sandbox Code Playgroud)

将由非readonly字段支持,并且可以随时通过任何可访问私有成员的代码进行分配.

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

对外界来说,私人制定者就好像它不存在一样.所以,它就像它真的不存在一样.


Tse*_*eng 12

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

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

例如:

public class MainViewModel 
{
    public INavigationService NavigationService { get; }

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

        NavigationService = navigationService;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用它时,您可以保证这个类不变量,并且它确保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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后可以编写代码(错误地或由没有经验的开发人员),NavigationService = null然后如果你没有空检查并访问它,你将得到一个NullReferenceException,如果没有处理你的应用程序崩溃.

回到你的例子:如果ICommand...你通常不访问ViewModel中的命令,只分配它(通常在构造函数中或当你的视图模型的内容发生变化时,就像子视图模型改变了,你想要分配它)命令到父viewmodel命令属性).

如果是清单:

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

if(Screenshots != null) 
{
    Screenshots.Add(new Screenshot(...));
}
Run Code Online (Sandbox Code Playgroud)

要么

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

Screenshots.Add(new Screenshot(...));
Run Code Online (Sandbox Code Playgroud)

再次,而不是总是使用

Screenshots.Add(new Screenshot(...));
Run Code Online (Sandbox Code Playgroud)

这有一个巨大的优势,你需要更少的代码,你的代码更易读,更易于维护,因为你不能"忘记"空检查和冒险NullReferenceException.

希望清理它.


小智 6

以下是编译器为您完成作业后属性的变化:


1 public ICommand AddCommand { get; } .:

private readonly ICommand <AddCommand>k__BackingField;
public ICommand AddCommand {
    get { return this.<AddCommand>k__BackingField; }
}
Run Code Online (Sandbox Code Playgroud)


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; } 
}
Run Code Online (Sandbox Code Playgroud)


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; } 
}
Run Code Online (Sandbox Code Playgroud)

简而言之,只能在构造函数中指定public get-only属性(因为该字段是只读的)或通过以下新语法:

public ICommand AddCommand { get; } = new MyCommand(); 
Run Code Online (Sandbox Code Playgroud)

但对于任何其他只读字段,此代码无论如何都放入构造函数中,因此没有太大区别:

public MyClass1()
{
    this.<AddCommand>k__BackingField = new MyCommand();
}
Run Code Online (Sandbox Code Playgroud)