web-dev-qa-db-fra.com

Pourquoi une propriété get-only remplacée reste-t-elle nulle lorsqu'elle est définie dans le constructeur de la classe de base?

J'ai essayé l'exemple suivant:

public class TestBase
{
    public virtual string ReadOnly { get; }

    public TestBase()
    {
        ReadOnly = "from base";
    }
}

class Test : TestBase
{
    public override string ReadOnly { get; }
    public Test()
    {
        // nothing here
    }
}

Lorsque je crée une instance de Test, je constate que ReadOnly reste null. Mais pourquoi? Je ne comprends vraiment pas ce qu'il se passe, quelqu'un pourrait-il m'expliquer, s'il vous plaît, pourquoi cela se produit? Au moins, je m'attendrais à une erreur, à savoir qu'une propriété en lecture seule ne puisse pas être définie en dehors de la classe propriétaire.

44
apfelstrudel24

Le compilateur traite cela comme ci-dessous; En gros, le code dans le constructeur écrit dans le champ de support original , dans TestBase. Il semble que votre scénario ne soit pas pris en charge, mais ... Je me demande si l’équipe linguistique a examiné ce cas.

BTW: si jamais vous voulez voir ce que le compilateur fait avec le code: sharplab.io

public class TestBase
{
    [CompilerGenerated]
    private readonly string <ReadOnly>k__BackingField; // note: not legal in "real" C#

    public virtual string ReadOnly
    {
        [CompilerGenerated]
        get
        {
            return <ReadOnly>k__BackingField; // the one in TestBase
        }
    }

    public TestBase()
    {
        <ReadOnly>k__BackingField = "from base";
    }
}
internal class Test : TestBase
{
    [CompilerGenerated]
    private readonly string <ReadOnly>k__BackingField;

    public override string ReadOnly
    {
        [CompilerGenerated]
        get
        {
            return <ReadOnly>k__BackingField; // the one in Test
        }
    }
}
38
Marc Gravell

La meilleure façon de l'expliquer consiste à examiner le code généré par le compilateur pour l'implémenter.

La classe de base est équivalente à ceci:

public class TestBase
{
    public virtual string ReadOnly => _testBaseReadOnly;

    public TestBase()
    {
        _testBaseReadOnly = "from base";
    }

    readonly string _testBaseReadOnly;
}

La classe dérivée est équivalente à ceci:

class Test : TestBase
{
    public override string ReadOnly => _testReadOnly;

    readonly string _testReadOnly;
}

La chose importante à noter ici est que la classe dérivée a son OWN BACKING FIELD pour ReadOnly - elle ne réutilise PAS celle de la classe de base.

Ayant réalisé cela, il devrait être évident pourquoi la propriété surchargée est null.

C'est parce que la classe dérivée a son propre champ de support pour ReadOnly et que son constructeur n'initialise pas ce champ de support.

Incidemment, si vous utilisez Resharper, cela vous avertira que vous ne définissez pas ReadOnly dans la classe dérivée:

 "Get-only auto-property 'ReadOnly' is never assigned."
17
Matthew Watson