2
votes

I've enabled Nullable References in my C# 8.0 project. I've also set all warnings as errors to force myself to address all such warnings. But one of them is confusing me. It is a dependency property that returns an interface pointer that might validly be null. Here is the property (inside a control called "LayerView")

private static readonly DependencyProperty ShapeBeingDrawnProperty = DependencyProperty.Register(
    nameof(ShapeBeingDrawn), 
    typeof(IShape), 
    typeof(LayerView),
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

public IShape ShapeBeingDrawn
{
    get => GetValue(ShapeBeingDrawnProperty) as IShape;  // RIGHT SIDE OF "=>" UNDERLINED IN RED
    set => SetValue(ShapeBeingDrawnProperty, value);
}

With nullable references enabled, this understandably causes a warning on the getter. The entire right side of the get is underlined in red and I get this build error

1>C:\Users\jmole\Documents\Dev\Mobile\Core\Views\LayerView.xaml.cs(429,13,429,16): error CS8766:
Nullability of reference types in return type of 'IShape? LayerView.ShapeBeingDrawn.get' doesn't 
match implicitly implemented member 'IShape ILayerView.ShapeBeingDrawn.get' (possibly because of 
nullability attributes).

First of all, what "implicitly" implemented member are they talking about? This is an explicitly implemented member. I am literally implementing it right here. But that aside, I tried to fix this as I've fixed many other such validly nullable properties: Make the property return a nullable reference

public IShape? ShapeBeingDrawn     // ONLY CHANGE IS HERE
{
    get => GetValue(ShapeBeingDrawnProperty) as IShape; // NOW, JUST "get" IS UNDERLINED IN RED
    set => SetValue(ShapeBeingDrawnProperty, value);
}

Unfortunately, this does not help. I still get the same error but this time the only part underlined in red is just the left side of the getter (the word "get").

Finally I was able to eliminate this error by changing the property back to a normal reference and using a hard cast in the getter

public IShape ShapeBeingDrawn
{
    get => (IShape)GetValue(ShapeBeingDrawnProperty);
    set => SetValue(ShapeBeingDrawnProperty, value);
}

But now, any code that tries to set this property to null (which is valid) gets a compiler error and I have to address that.

private void LayerView_OnKeyDown(object sender, KeyEventArgs e)
{
    // Don't do anything if we've disabled shape mouse/touch input.

    if (e.Key != Key.Escape)
        return;

    UnselectAll();
    ShapeBeingDrawn = null;  // COMPILER NO LIKEY
}

So what is the proper way to integrate nullable references and dependency properties? Should I just #pragma warning disable around such problems? I'd like to avoid that if I can.

1
Does property IShape ShapeBeingDrawn is inherited from another interface? If it does, then how is it declared in that interface? I was able to reproduce your problem only when the property ShapeBeingDrawn was inherited from another inteface, and to fix it I had to ensure that both in inteface and in the class this property was declared nullable or not nullable. Please, check this sample: dotnetfiddle.net/cOQY4F.Iliar Turdushev
Yep, that's exactly what it was. Confusing error message on the surface but now that I understand it, it makes perfect sense. And if you actually went to all the trouble of trying to reproduce it, I am all the more appreciative. Thank you.Joe

1 Answers

5
votes

TL;DR

The problem occurs because property ShapeBeingDrawn is declared as non-nullable reference type IShape in the interface ILayerView, but in the class LayerView it is implemented as nullable reference type IShape?. Therefore compiler complains that return type of the property in the class does not match the return type in the interface.

To fix the problem we should ensure that both the interface and the class declare the property as nullable or non-nullable. If in one of them the declaration differs then we will again get compiler error.


Explanation

Why does the problem occur?

error CS8766: Nullability of reference types in return type of 'IShape? LayerView.ShapeBeingDrawn.get' doesn't match implicitly implemented member 'IShape ILayerView.ShapeBeingDrawn.get' (possibly because of nullability attributes).

From the error message we can see that there is an interface ILayerView with a non-nullable property ShapeBeignDrawn:

interface ILayerView
{
    IShape ShapeBeingDrawn { get; set; }
}

and a class LayerView that implements this interface but declares property ShapeBeignDrawn as nullable:

public class LayerView
{
    public IShape? ShapeBeingDrawn
    {
        get => GetValue(ShapeBeingDrawnProperty) as IShape;
        set => SetValue(ShapeBeingDrawnProperty, value);
    }
}

So the interface declares property as non-nullable but the class implements it as nullable. Therefore compiler complains that return type of the getter of the property in the class does not match the return type in the interface.

Question:

First of all, what "implicitly" implemented member are they talking about?

Answer:

When we declare a property compiler implicitly generates for it two methods: get_PropertyName and set_PropertyName. Therefore compiler complains for the return type of the implicitly implemented getter method.


How should we fix the problem?

To fix the problem we should ensure that both an interface and a class declare a property as nullable or non-nullable. If in one of them the declaration differs then we will again get compiler error CS8766.

Here is a code sample that reproduces the problem and gives a hint how to fix it.