C#属性总是有“幕后”备份字段吗?

6

我知道在C#中使用属性时,编译器会为它们生成CIL中的getter和setter(即get_PropertyName和set_PropertyName)。例如,考虑以下代码:

    class Program
    {
        class Test
        {
            public string Name { get; set; }
        }
        static void Main(string[] args)
        {
            //Here I'm using reflection to inspect methods of Test class
            Type type = typeof(Test);
            foreach (var item in type.GetMethods())
            {
                Console.WriteLine(item.Name);
            }
        }
    } 

这个程序将使用Test的方法生成输出,其中包括我所说的getter和setter:get_Nameset_Name

据我理解,如果getter和setter是在“幕后”创建的,那么也应该创建一个支持字段,从中获取/设置值。

因此,从前面的例子中,我可以使用反射来检查Test类的字段,就像这样:

    static void Main(string[] args)
    {
        Type type = typeof(Test);
        foreach (var item in type.GetFields())
        {
            Console.WriteLine(item.Name);
        }
    } 

这个程序的输出为空,我猜测原因是创建的后备字段具有“private”访问权限,因此我们无法看到它。但是由于我不知道如何检查它,所以您能否告诉我一个后备字段是否总是被创建(即使我们只有一个简单的属性,仅具有“get;”和“set;”)?

据我所知,他们确实这样做。 - Alex
5
为了让您更轻松地找到其他参考资料:这些通常被称为“backing”字段,而不是“backup”字段。 - Jon Skeet
有趣的是,后来版本中添加的一个功能已经盖过了它们的主要用途。 - IS4
2个回答

14

如果您指的是简单属性,比如:

{get;set;}
或者:
{get;}

是的,存在一个字段;在您的GetFields()调用中添加BindingFlags.NonPublic | BindingFlags.Instance,您就会看到它:

foreach (var item in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
    Console.WriteLine(item.Name);
}

通常它们有一个涉及 <> 的难以发音的名称 - 在我的机器上,您的名称是 <Name>k__BackingField - 但是: 这个名称是编译器功能(虽然许多序列化等库会使用它,因此不太可能改变)。

但是: 不,属性本身并不总是涉及字段;例如:

public int Value => 42; // no field
或者
public int Name { get { return obj.Name; } set { obj.Name = value; } }

13

但是我不知道如何检查它,你能告诉我备份字段是否总是会被创建(即使我们只有一个简单的属性,只有get;和set;吗?

自动实现的属性,例如:

public int ReadWriteProperty { get; set; }
public int ReadOnlyProperty { get; }

编译器会生成支持字段来支持属性。但是如果您提供自己的getter/setter实现,编译器将不会生成字段:

public int Zero => 0;

public int IgnoresSetter
{
    get { return 10; }
    set { /* Meh. Whatever you say, I'm still going to return 10 in the getter... */ }
}

这两者都不会导致生成一个字段。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接