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