【发布时间】:2008-12-04 13:10:50
【问题描述】:
如果在 C# 自动属性中 get 和 set 都是强制性的,我为什么还要费心指定“get; set;”有吗?
【问题讨论】:
标签: c# c#-3.0 automatic-properties
如果在 C# 自动属性中 get 和 set 都是强制性的,我为什么还要费心指定“get; set;”有吗?
【问题讨论】:
标签: c# c#-3.0 automatic-properties
因为您可能需要一个只读属性:
public int Foo { get; private set; }
或只写属性:
public int Foo { private get; set; }
【讨论】:
错误:属性或索引器不能作为 out 或 ref 参数传递
如果您没有指定{get; set;},那么编译器将不知道它是字段还是属性。
这很重要,因为当它们“看起来”相同时,编译器会以不同的方式对待它们。例如对属性调用“InitAnInt”会引发错误。
class Test
{
public int n;
public int i { get; set; }
public void InitAnInt(out int p)
{
p = 100;
}
public Test()
{
InitAnInt(out n); // This is OK
InitAnInt(out i); // ERROR: A property or indexer may not be passed
// as an out or ref parameter
}
}
您不应该在类上创建公共字段/变量,您永远不知道何时将其更改为具有 get 和 set 访问器,然后您不知道要破坏哪些代码,尤其是如果您有针对您的 API 进行编程的客户端。
您还可以对 get & set 使用不同的访问修饰符,例如{得到; private set;} 使 get public 和 set 对声明类私有。
【讨论】:
只是想我会分享我在这个主题上的发现。
像下面这样编写一个属性,是一个 .net 3.0 快捷方式调用“自动实现的属性”。
public int MyProperty { get; set; }
这可以为您节省一些打字时间。声明一个属性的方法是这样的:
private int myProperty;
public int MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
当您使用“自动实现的属性”时,编译器会生成代码来连接 get 并设置为某个“k_BackingField”。下面是使用 Reflector 的反汇编代码。
public int MyProperty
{
[CompilerGenerated]
get
{
return this.<MyProperty>k__BackingField;
}
[CompilerGenerated]
set
{
this.<MyProperty>k__BackingField = value;
}
}
从 IL 中反汇编的 C# 代码
还为 setter 和 getter 连接了一个方法。
[CompilerGenerated]
public void set_MyProperty(int value)
{
this.<MyProperty>k__BackingField = value;
}
[CompilerGenerated]
public int get_MyProperty()
{
return this.<MyProperty>k__BackingField;
}
从 IL 中反汇编的 C# 代码
当你声明一个只读的自动实现属性时,通过将 setter 设置为私有:
public int MyProperty { get; private set; }
所有编译器都会将“set”标记为私有。 setter 和 getter 方法说的一样。
public int MyProperty
{
[CompilerGenerated]
get
{
return this.<MyProperty>k__BackingField;
}
private [CompilerGenerated]
set
{
this.<MyProperty>k__BackingField = value;
}
}
从 IL 中反汇编的 C# 代码
所以我不确定为什么框架需要 get;并设置;在自动实现的属性上。如果没有提供 set 和 setter 方法,他们可能就不会编写它。但是可能有一些编译器级别的问题使这变得困难,我不知道。
如果您查看声明只读属性的漫长过程:
public int myProperty = 0;
public int MyProperty
{
get { return myProperty; }
}
然后看反汇编代码。 setter 根本不存在。
public int Test2
{
get
{
return this._test;
}
}
public int get_Test2()
{
return this._test;
}
从 IL 中反汇编的 C# 代码
【讨论】:
因为您需要一些方法来将其与普通字段区分开来。
拥有不同的访问修饰符也很有用,例如
public int MyProperty { get; private set; }
【讨论】:
编译器需要知道您是否希望它生成 getter 和/或 setter,或者可能正在声明一个字段。
【讨论】:
如果属性没有访问器,编译器如何将它与字段分开?什么将它与字段分开?
【讨论】:
好吧,显然您需要一种消除字段和属性之间歧义的方法。但是必填关键字真的有必要吗?例如,很明显这两个声明是不同的:
public int Foo;
public int Bar { }
这可能行得通。也就是说,这是一种编译器可以理解的语法。
但是你会遇到一个空块具有语义意义的情况。这似乎很不稳定。
【讨论】:
由于没有人提到它...您可以将自动属性设为虚拟并覆盖它:
public virtual int Property { get; set; }
如果没有get/set,它会如何被覆盖?请注意,您可以override the getter and not the setter:
public override int Property { get { return int.MinValue; } }
【讨论】:
此外,因为从 C# 6.0 开始(在 Visual Studio 2015 中,在最终预览版中提供此答案时),您可以实现真正的只读属性:
public string Name { get; }
public string Name { get; } = "This won't change even internally";
... 与当前使用公共 getter/private setter 对的不完美解决方法相反:
public string Name { get; private set; }
public Constructor() { Name="As initialised"; }
public void Method() { Name="This might be changed internally. By mistake. Or not."; }
上面的例子如下(编译可执行在线here)。
using System;
public class Propertier {
public string ReadOnlyPlease { get; private set; }
public Propertier() { ReadOnlyPlease="As initialised"; }
public void Method() { ReadOnlyPlease="This might be changed internally"; }
public override string ToString() { return String.Format("[{0}]",ReadOnlyPlease); }
}
public class Program {
static void Main() {
Propertier p=new Propertier();
Console.WriteLine(p);
// p.ReadOnlyPlease="Changing externally!";
// Console.WriteLine(p);
// error CS0272: The property or indexer `Propertier.ReadOnlyPlease' cannot be used in this context because the set accessor is inaccessible
// That's good and intended.
// But...
p.Method();
Console.WriteLine(p);
}
}
有关 C# 6.0 的其他精彩新闻可作为官方预览视频here。
【讨论】: