48

从此

public int MyInt{ get; set;}

相当于

private int _myInt;
public int MyInt{ get{return _myInt;} set{_myInt = value;} }

当您将自动属性设为虚拟时

public virtual int MyInt{ get; set;}

然后在子类中覆盖此属性

public override int MyInt{ get{return someVar;} set{someVar = value;} }

这个子类现在有不受欢迎的隐藏分配 _myInt 吗?

4

4 回答 4

55

简短回答:是的,Child分配了所有Base类字段,所以它仍然分配了支持字段。Base.MyInt但是,除了通过属性之外,您无法通过其他任何方式访问它

长答案

快速拆卸结果。

BaseChild类实现:

public class Base
{
    public virtual int MyInt { get; set; }
}

public class Child : Base
{
    private int anotherInt;

    public override int MyInt
    {
        get { return anotherInt; }
        set { anotherInt = value; }
    }
}

在此处输入图像描述

如您所见,支持字段存在于Base类中。但是,它是私有的,因此您不能从Child类中访问它:

.field private int32 '<MyInt>k__BackingField'

而且您的Child.MyInt财产不使用该字段。属性 IL 是:

.method public hidebysig specialname virtual 
    instance int32 get_MyInt () cil managed 
{
    // Method begins at RVA 0x2109
    // Code size 7 (0x7)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldfld int32 ConsoleApplication2.Child::anotherInt
    IL_0006: ret
} // end of method Child::get_MyInt

.method public hidebysig specialname virtual 
    instance void set_MyInt (
        int32 'value'
    ) cil managed 
{
    // Method begins at RVA 0x2111
    // Code size 8 (0x8)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldarg.1
    IL_0002: stfld int32 ConsoleApplication2.Child::anotherInt
    IL_0007: ret
} // end of method Child::set_MyInt

anotherInt正如您所料,是使用字​​段。

访问'<MyInt>k__BackingField'(间接,通过Base.MyInt属性)的唯一方法是:

  • base.MyIntChild班内
于 2013-08-12T08:21:02.137 回答
6

它不仅相当于它的实际实现。编译器在其预编译阶段重写您的自动属性。尽管字段名称将被命名为其他名称。

因此,行为将与您手动创建属性相同。

是的,隐藏字段将存在,但不会分配给它,因为您的覆盖不会调用基本实现。

如果您将覆盖更改为

public override int MyInt
{
  get { return someVar; }
  set { 
    someVar = value;
    base.MyInt = value
  }
}

然后会发生分配

于 2013-08-12T08:16:00.303 回答
3

是的,就像它没有被定义为自动属性一样。

基类中需要分配,因为它仍然需要存在并且有用。基类不知道派生类的存在,派生类可以在其定义中使用支持字段

如果您定义了一个基类和派生类,如下所示:

public class Base
{
  public virtual string Name {get; set;}
}

public class Derived : Base
{
  private string _name;

  public override string Name 
  {
    get { 
      return _name; 
    }
    set 
    { 
      //access the base property we are overriding
      base.Name = value + " from derived";
      _name = value;
    }
  }
}

您可以使用反射来查看基类的支持字段是否确实存在,并且行为符合预期:

Type tbase = typeof(Base);
FieldInfo fi = tbase.GetField("<Name>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance);

Base b = new Base {Name = "Test"};

string baseValue = fi.GetValue(b).ToString();
Console.WriteLine(baseValue); //gives "Test";

Derived d = new Derived {Name = "Test" };

string derivedValue = fi.GetValue(d).ToString();
Console.WriteLine(derivedValue); //gives "Test from derived";

实际支持字段的名称是一个未记录的实现细节,所以我不会在任何生产代码中使用它。(我通过使用 LINQPad 的 IL 视图得到它)

于 2013-08-12T08:14:26.430 回答
2

MyInt 字段将在那里,它需要!编译器无法根据子类信息进行优化。例如考虑派生类可能不会出现在打包的运行程序中

更新,因为我误读了部分问题。感谢@PVitt 指出。

于 2013-08-12T08:27:04.247 回答