CS8766: возможность обнуления ссылочных типов в возвращаемом типе не соответствует неявно реализованному члену - PullRequest
1 голос
/ 28 мая 2020

Я включил ссылки, допускающие значение NULL, в моем проекте C# 8.0. Я также установил все предупреждения как ошибки, чтобы заставить себя реагировать на все такие предупреждения. Но один из них меня сбивает. Это свойство зависимости, которое возвращает указатель интерфейса, который действительно может иметь значение NULL. Вот свойство (внутри элемента управления под названием «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);
}

При включенных ссылках, допускающих значение NULL, это по понятным причинам вызывает предупреждение на геттере. Вся правая часть get подчеркнута красным, и я получаю эту ошибку сборки

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).

Прежде всего, о каком «неявно» реализованном элементе идет речь? Это явно реализованный член. Я буквально реализую это прямо здесь. Но помимо этого, я попытался исправить это, так как исправил многие другие свойства, допускающие значение NULL: Сделать так, чтобы свойство возвращало ссылку, допускающую значение NULL.

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

К сожалению, это не помогает. Я все еще получаю ту же ошибку, но на этот раз единственная часть, подчеркнутая красным, - это только левая сторона геттера (слово «get»).

Наконец, я смог устранить эту ошибку, вернув свойство обратно на обычную ссылку и с использованием жесткого приведения в геттере

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

Но теперь любой код, который пытается установить для этого свойства значение null (которое является допустимым), получает ошибку компилятора, и я должен исправить это.

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
}

Итак, как правильно интегрировать ссылки, допускающие значение NULL, и свойства зависимостей? Должен ли я просто отключить предупреждение #pragma при таких проблемах? Я бы хотел этого избежать, если смогу.

1 Ответ

1 голос
/ 01 июня 2020

TL; DR

Проблема возникает из-за того, что свойство ShapeBeingDrawn объявлено как не обнуляемое ссылочный тип IShape в интерфейсе ILayerView, но в классе LayerView реализован как обнуляемый ссылочный тип IShape?. Поэтому компилятор жалуется, что тип возвращаемого значения свойства в классе не соответствует типу возвращаемого значения в интерфейсе.

Чтобы устранить проблему, мы должны убедиться, что и интерфейс, и класс объявляют свойство как nullable или non-nullable. Если в одном из них объявление отличается, то мы снова получим ошибку компилятора.

Объяснение

Почему возникает проблема?

ошибка CS8766: возможность обнуления ссылочных типов в возвращаемом типе 'IShape? LayerView.ShapeBeingDrawn.get' не соответствует неявно реализованному члену 'IShape ILayerView.ShapeBeingDrawn.get' (возможно, из-за атрибутов, допускающих значение NULL).

Из сообщения об ошибке мы видим, что существует интерфейс ILayerView со свойством non-nullable ShapeBeignDrawn:

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

и класс LayerView, который реализует этот интерфейс, но объявляет свойство ShapeBeignDrawn как nullable:

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

Таким образом, интерфейс объявляет свойство как non-nullable но класс реализует это как nullable. Поэтому компилятор жалуется, что тип возвращаемого значения getter свойства в классе не соответствует типу возвращаемого значения в интерфейсе.

Вопрос:

Прежде всего, о каком «неявно» реализованном элементе идет речь?

Ответ:

Когда мы объявляем свойство, компилятор неявно генерирует для него два метода : get_PropertyName и set_PropertyName. Поэтому компилятор жалуется на возвращаемый тип неявно реализованного метода getter.

Как мы должны решить проблему?

Чтобы решить проблему, мы должны убедиться, что и интерфейс, и класс объявляют свойство как nullable или non-nullable. Если в одном из них объявление отличается, то мы снова получим ошибку компилятора CS8766.

Вот пример кода , который воспроизводит проблему и дает подсказку, как ее исправить.

...