9

I came across a behavior that surprises me. Given the following two classes:

class Parent
{
    public virtual bool Property { get; set; }
}

class Child : Parent
{
    public override bool Property { get => base.Property; }
}

I can write code like this:

Child child = new Child();
child.Property = true; // this is allowed

The IDE makes it confusing, too, because, while it allows the assignment, it also indicates that the overridden property is read-only:

enter image description here

Furthermore, this override is only allowed when I'm using the base class' getter:

enter image description here

What is going on here?

rory.ap
  • 34,009
  • 10
  • 83
  • 174
  • I'm not qualified to provide a real answer, but my assumption is the compiler generates `set_Property` and `get_Property` methods, then overrides only `get_Property` for `Child`. – Scott May 16 '18 at 17:51
  • seems to allow you to only override the getter, make a protected/private setter? – kenny May 16 '18 at 17:51
  • It would be interesting to see the actual IL output, I haven't messed with 7.3 yet but I can't see how the IL would be that much different... – Ron Beyer May 16 '18 at 17:53
  • The message ("Auto-implemented properties must override all accessors") suggests that you don't _have_ to use the base getter - you just can't use auto-implemented properties (`get;`) that way. – C.Evenhuis May 16 '18 at 17:56
  • 2
    Anyone have the Jon Skeet bat signal? I'd love it if he'd weigh in. – rory.ap May 16 '18 at 18:36
  • @JohnyL -- There's nothing abstract. – rory.ap May 16 '18 at 19:21
  • Let me guess, that assignment is in the constructor. – Paulo Morgado May 16 '18 at 22:49
  • @PauloMorgado -- no. This is in the Main method of a console app. – rory.ap May 17 '18 at 11:16
  • Can you provide a complete example instead of images. – Paulo Morgado May 17 '18 at 14:54
  • 1
    @PauloMorgado The whole first half of the question has the code. The picture only shows the behavior the OP is seeing. The code is minimal, verifiable/reproducible, and completely shows the behavior that they are seeing along with how it differs from what they were expecting. Looks like a complete example to me. What exactly are you looking for the op to add? – Sudsy1002 May 17 '18 at 19:35

1 Answers1

8

I'll take a crack at this.

It looks like this may just be a bug with Intellisense, where it is unable to find the base implementation of an auto-property. The code is valid and makes sense - here's another way to express your example.

Child child = new Child();
child.SetProperty(true);

class Parent
{
    private bool _property;

    public virtual bool GetProperty() => _property;
    public virtual void SetProperty(bool value) => _property = value;
}

class Child : Parent
{
    public override bool GetProperty() => base.GetProperty();
}

With this representation, it's now obvious why overriding GetProperty is fine. Here's the relevant IL for your code:

Main:
IL_0000:  newobj      Child..ctor
IL_0005:  ldc.i4.1
IL_0006:  callvirt    Parent.set_Property
IL_000B:  ret

Parent.get_Property:
IL_0000:  ldarg.0
IL_0001:  ldfld       Parent.<Property>k__BackingField
IL_0006:  ret

Parent.set_Property:
IL_0000:  ldarg.0
IL_0001:  ldarg.1
IL_0002:  stfld       Parent.<Property>k__BackingField
IL_0007:  ret

Parent..ctor:
IL_0000:  ldarg.0
IL_0001:  call        System.Object..ctor
IL_0006:  ret

Child.get_Property:
IL_0000:  ldarg.0
IL_0001:  call        Parent.get_Property
IL_0006:  ret

Child..ctor:
IL_0000:  ldarg.0
IL_0001:  call        Parent..ctor
IL_0006:  ret

And here's my version:

Main:
IL_0000:  newobj      Child..ctor
IL_0005:  ldc.i4.1
IL_0006:  callvirt    Parent.SetProperty
IL_000B:  ret

Parent.GetProperty:
IL_0000:  ldarg.0
IL_0001:  ldfld       Parent._property
IL_0006:  ret

Parent.SetProperty:
IL_0000:  ldarg.0
IL_0001:  ldarg.1
IL_0002:  stfld       Parent._property
IL_0007:  ret

Parent..ctor:
IL_0000:  ldarg.0
IL_0001:  call        System.Object..ctor
IL_0006:  ret

Child.GetProperty:
IL_0000:  ldarg.0
IL_0001:  call        Parent.GetProperty
IL_0006:  ret

Child..ctor:
IL_0000:  ldarg.0
IL_0001:  call        Parent..ctor
IL_0006:  ret     

Note that this is different than public override bool Property { get; }, being shorthand for instructing the compiler to generate a single getter override for a backing property of the same name, with no mention of the preexisting setter. Somebody experienced with the actual spec will definitely be able to offer more information around this, however.

Scott
  • 5,338
  • 5
  • 45
  • 70
  • I don't know that the read-only auto property will appear in the official spec as the feature is a part of C# 6 and the spec covers up through C# 5, so it may be tricky to track down a canonical answer. – Jonathon Chase May 16 '18 at 18:16
  • So essentially what the IL is showing us is that a property is really just syntactic sugar for two methods like in your example. My first example -- the one that works -- is simply overriding the getter and leaving the setter intact (inheriting from the parent). The second example that doesn't work is like overriding the getter AND the setter and saying the setter really doesn't exist. – rory.ap May 16 '18 at 18:28
  • 1
    @rory.ap As far as I can tell, that's exactly what's happening. You can even confirm that the auto-property gets translated to a backing field + methods by directly calling `get_Property` and `set_Property` with reflection at runtime. The same thing happens with operator overloads; just special hidden methods. – Scott May 16 '18 at 18:33
  • Well, I'll certainly submit my case to MS as feedback for the VS2017 preview I'm using. At least for the intellisense confusion. Thanks for your help. – rory.ap May 16 '18 at 18:36