Condividi tramite


Proprietà parziali

Nota

Questo articolo è una specifica di funzionalità. La specifica funge da documento di progettazione per la funzionalità. Include le modifiche specifiche proposte, insieme alle informazioni necessarie durante la progettazione e lo sviluppo della funzionalità. Questi articoli vengono pubblicati fino a quando le modifiche specifiche proposte non vengono completate e incorporate nella specifica ECMA corrente.

Potrebbero verificarsi alcune discrepanze tra la specifica di funzionalità e l'implementazione completata. Tali differenze vengono catturate nelle note pertinenti del language design meeting (LDM).

Altre informazioni sul processo per l'adozione di speclet di funzionalità nello standard del linguaggio C# sono disponibili nell'articolo sulle specifiche di .

Problema del campione: https://github.com/dotnet/csharplang/issues/6420

Grammatica

La grammatica property_declaration(§14.7.1) viene aggiornata come segue:

property_declaration
-    : attributes? property_modifier* type member_name property_body
+    : attributes? property_modifier* 'partial'? type member_name property_body
    ;  

Osservazioni: È per certi versi simile a come method_header(§15.6.1) e class_declaration(§15.2.1) sono specificati. Si noti che problema 946 propone di ridurre il requisito di ordinamento e probabilmente si applica a tutte le dichiarazioni che consentono il modificatore partial. Si intende specificare un tale relax di ordinamento nel prossimo futuro e implementarlo nella stessa versione implementata da questa funzionalità.

Definizione e implementazione di dichiarazioni

Quando una dichiarazione di proprietà include un modificatore parziale, tale proprietà viene considerata una proprietà parziale . Le proprietà parziali possono essere dichiarate solo come membri di tipi parziali.

Una dichiarazione di proprietà parziale viene definita dichiarazione di definizione quando i relativi accessori hanno tutti un corpo costituito da un punto e virgola e manca il modificatore extern. In caso contrario, si tratta di una dichiarazione di implementazione .

partial class C
{
    // Defining declaration
    public partial string Prop { get; set; }

    // Implementing declaration
    public partial string Prop { get => field; set => field = value; }
}

Poiché la sintassi con i corpi di accesso separati da punto e virgola è riservata per la dichiarazione di definizione , una proprietà parziale non può essere implementata automaticamente . Di conseguenza, regoliamo le proprietà implementate automaticamente (§15.7.4) come segue:

Una proprietà implementata automaticamente (o automaticamente per breve), è una proprietà non astratta, non extern, non parziale, proprietà non ref-valued con corpi di accesso solo punto e virgola.

Osservazioni. È utile che il compilatore sia in grado di esaminare una singola dichiarazione in isolamento e sapere se si tratta di una definizione o di una dichiarazione di implementazione. Pertanto, non vogliamo consentire le proprietà automatiche includendo due dichiarazioni di proprietà partial identiche, ad esempio. Non pensiamo che i casi d'uso per questa funzionalità implicano l'implementazione della proprietà parziale con una proprietà automatica, ma nei casi in cui si desidera un'implementazione semplice, pensiamo che la parola chiave field rende le cose abbastanza semplici.


Una proprietà parziale deve avere una dichiarazione che definisce e una dichiarazione che implementa.

Osservazioni. Non riteniamo inoltre utile consentire la suddivisione della dichiarazione in più di due parti, per consentire l'implementazione di funzioni di accesso diverse in posizioni diverse, ad esempio. Pertanto, è sufficiente imitare lo schema stabilito da metodi parziali.


Solo la dichiarazione di definizione di una proprietà parziale partecipa alla ricerca, analogamente a come solo la dichiarazione di definizione di un metodo parziale partecipa alla risoluzione dell'overload.

Osservazioni. Nel compilatore si prevede che solo il simbolo per la dichiarazione di definizione venga visualizzato nell'elenco dei membri e che sia possibile accedere al simbolo per la parte di implementazione tramite il simbolo di definizione. Tuttavia, alcune funzionalità come l'analisi nullable potrebbero vedere tramite alla dichiarazione di implementazione per fornire un comportamento più utile.

partial class C
{
    public partial string Prop { get; set; }
    public partial string Prop { get => field; set => field = value; }

    public C() // warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
    {
    }
}

Una proprietà parziale non può avere il modificatore abstract.

Una proprietà parziale non può implementare in modo esplicito le proprietà dell'interfaccia.

Unione di attributi

Analogamente ai metodi parziali, gli attributi nella proprietà risultante sono gli attributi combinati delle parti vengono concatenati in un ordine non specificato e i duplicati non vengono rimossi.

Attributi di informazioni sul chiamante

La lingua seguente viene modificata dallo standard :

È un errore avere lo stesso attributo di caller-info su un parametro sia nella parte definita che in quella implementata di una dichiarazione di metodo parziale membro. Vengono applicati solo gli attributi caller-info nella sezione di definizione, mentre gli attributi caller-info che si verificano solo nella sezione di implementazione vengono ignorati.

  • L'errore descritto non rientra nelle definizioni di questi attributi che non hanno AllowMultiple = true. Usandoli più volte, incluse le dichiarazioni parziali, viene generato un errore.
  • Quando gli attributi caller-info vengono applicati a un parametro nella parte di implementazione di un metodo parziale, il compilatore Roslyn segnala un avviso. Verrà inoltre segnalato un avviso per lo stesso scenario in una proprietà parziale.

Firme corrispondenti

La riunione LDM 14 settembre 2020 definito un set di requisiti "rigorosi" per la corrispondenza della firma dei metodi parziali, introdotti in un'onda di avviso. Le proprietà parziali hanno requisiti analoghi ai metodi parziali per la corrispondenza delle firme il più possibile, ad eccezione del fatto che tutte le diagnostica per la mancata corrispondenza vengono segnalate per impostazione predefinita e non vengono mantenute dietro un'onda di avviso.

I requisiti di corrispondenza della firma includono:

  1. Differenze tra tipi e tipi di riferimento tra dichiarazioni di proprietà parziali che sono significative per il runtime generano un errore in fase di compilazione.
  2. Le differenze nei nomi degli elementi di tupla all'interno di dichiarazioni di proprietà parziali generano un errore in fase di compilazione, come per i metodi parziali.
  3. Le dichiarazioni di proprietà e le relative dichiarazioni di accesso devono avere gli stessi modificatori, anche se i modificatori possono essere visualizzati in un ordine diverso.
    • Eccezione: questo non si applica al modificatore extern, che può apparire solo in una dichiarazione di implementazione .
  4. Tutte le altre differenze sintattiche nelle firme delle dichiarazioni di proprietà parziali generano un avviso in fase di compilazione, con le eccezioni seguenti:
    • Gli elenchi di attributi nelle o all'interno delle dichiarazioni di proprietà parziali non devono corrispondere. Viene invece eseguita l'unione di attributi nelle posizioni corrispondenti, per unione di attributi.
    • Le differenze di contesto nullable non causano avvisi. In altre parole, una differenza in cui uno dei tipi è nullable-oblivious e l'altro tipo è nullable-annotated o not-nullable-annotated non genera alcun avviso.
    • I valori dei parametri predefiniti non devono corrispondere. Un avviso viene segnalato quando la parte di implementazione di un indicizzatore parziale ha valori di parametro predefiniti. Questo comportamento è simile a un avviso esistente che si verifica quando la parte di implementazione di un metodo parziale ha valori di parametro predefiniti.
  5. Un avviso si verifica quando i nomi dei parametri differiscono tra la definizione e l'implementazione delle dichiarazioni. I nomi dei parametri della parte di definizione vengono usati nei siti di utilizzo e in emit.
  6. Le differenze di nullità che non comportano l'obliviosa nullità generano avvisi. Quando si analizza un corpo di un accessor, viene usata la firma dell'implementazione. La firma della parte della definizione viene usata quando si analizzano i siti di utilizzo e durante l'emissione. Questo è coerente con i metodi parziali.
partial class C1
{
    public partial string Prop { get; private set; }

    // Error: accessor modifier mismatch in 'set' accessor of 'Prop'
    public partial string Prop { get => field; set => field = value; }
}

partial class C2
{
    public partial string Prop { get; init; }

    // Error: implementation of 'Prop' must have an 'init' accessor to match definition
    public partial string Prop { get => field; set => field = value; }
}

partial class C3
{
    public partial string Prop { get; }

    // Error: implementation of 'Prop' cannot have a 'set' accessor because the definition does not have a 'set' accessor.
    public partial string Prop { get => field; set => field = value; }
}

partial class C4
{
    public partial string this[string s = "a"] { get; set; }
    public partial string this[string s] { get => s; set { } } // ok

    public partial string this[int i, string s = "a"] { get; set; }
    public partial string this[int i, string s = "a"] { get => s; set { } } // CS1066: The default value specified for parameter 's' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
}

Commenti sulla documentazione

Vogliamo che il comportamento dei commenti della documentazione sulle proprietà parziali sia coerente con quello che abbiamo fornito per i metodi parziali. Questo comportamento è dettagliato in https://github.com/dotnet/csharplang/issues/5193.

È consentito includere commenti del documento sulla definizione o sulla parte di implementazione di una proprietà parziale. Si noti che i commenti dei documenti non sono supportati nelle funzioni di accesso alle proprietà.

Quando i commenti del documento sono presenti solo in una delle parti della proprietà, questi commenti vengono usati normalmente (visualizzati tramite ISymbol.GetDocumentationCommentXml(), scritti nel file XML della documentazione e così via).

Quando i commenti del documento sono presenti in entrambe le parti, tutti i commenti del documento nella parte di definizione vengono eliminati e vengono usati solo i commenti del documento nella parte di implementazione.

Ad esempio, il programma seguente:

/// <summary>
/// My type
/// </summary>
partial class C
{
    /// <summary>Definition part comment</summary>
    /// <returns>Return value comment</returns>
    public partial int Prop { get; set; }
    
    /// <summary>Implementation part comment</summary>
    public partial int Prop { get => 1; set { } }
}

Risultati nel file di documentazione XML seguente:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApp1</name>
    </assembly>
    <members>
        <member name="T:C">
            <summary>
            My type
            </summary>
        </member>
        <member name="P:C.Prop">
            <summary>
            Implementation part comment
            </summary>
        </member>
    </members>
</doc>

Quando i nomi dei parametri differiscono tra dichiarazioni parziali, <paramref> elementi usano i nomi dei parametri della dichiarazione associata al commento della documentazione nel codice sorgente. Ad esempio, un parametro paramref in un commento doc inserito in una dichiarazione di implementazione fa riferimento ai simboli dei parametri nella dichiarazione di implementazione usando i relativi nomi di parametro. Questo è coerente con i metodi parziali.

/// <summary>
/// My type
/// </summary>
partial class C
{
    public partial int this[int x] { get; set; }

    /// <summary>
    /// <paramref name="x"/> // warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'x', but there is no parameter by that name
    /// <paramref name="y"/> // ok. 'Go To Definition' will go to 'int y'.
    /// </summary>
    public partial int this[int y] { get => 1; set { } } // warning CS9256: Partial property declarations 'int C.this[int x]' and 'int C.this[int y]' have signature differences.
}

Risultati nel file di documentazione XML seguente:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApp1</name>
    </assembly>
    <members>
        <member name="T:C">
            <summary>
            My type
            </summary>
        </member>
        <member name="P:C.Item(System.Int32)">
            <summary>
            <paramref name="x"/> // warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'x', but there is no parameter by that name
            <paramref name="y"/> // ok. 'Go To Definition' will go to 'int y'.
            </summary>
        </member>
    </members>
</doc>

Ciò può generare confusione, perché la firma dei metadati userà i nomi dei parametri della parte di definizione. È consigliabile assicurarsi che i nomi dei parametri corrispondano tra le parti per evitare questa confusione.

Indicizzatori

Per riunione LDM il 2 novembre 2022, gli indicizzatori saranno supportati con questa funzionalità.

La grammatica degli indicizzatori viene modificata nel modo seguente:

indexer_declaration
-    : attributes? indexer_modifier* indexer_declarator indexer_body
+    : attributes? indexer_modifier* 'partial'? indexer_declarator indexer_body
-    | attributes? indexer_modifier* ref_kind indexer_declarator ref_indexer_body
+    | attributes? indexer_modifier* 'partial'? ref_kind indexer_declarator ref_indexer_body
    ;

I parametri dell'indicizzatore parziale devono corrispondere a tutte le dichiarazioni in base alle stesse regole di firme corrispondenti. La combinazione degli attributi viene eseguita tra i parametri dell'indicizzatore parziale.

partial class C
{
    public partial int this[int x] { get; set; }
    public partial int this[int x]
    {
        get => this._store[x];
        set => this._store[x] = value;
    }
}

// attribute merging
partial class C
{
    public partial int this[[Attr1] int x]
    {
        [Attr2] get;
        set;
    }

    public partial int this[[Attr3] int x]
    {
        get => this._store[x];
        [Attr4] set => this._store[x] = value;
    }

    // results in a merged member emitted to metadata:
    public int this[[Attr1, Attr3] int x]
    {
        [Attr2] get => this._store[x];
        [Attr4] set => this._store[x] = value;
    }
}

Problemi aperti

Altri tipi di membri

Un membro della comunità ha aperto una discussione per richiedere l'assistenza per gli eventi parziali . Nella riunione LDM il 2 novembre 2022, abbiamo deciso di rimandare il supporto per gli eventi, in parte perché nessuno al tempo l'aveva richiesto. Potremmo voler rivedere questa domanda, dal momento che questa richiesta è ora arrivata, ed è trascorso più di un anno dall'ultima discussione.

Potremmo anche spingerci oltre consentendo dichiarazioni parziali di costruttori, operatori, campi e così via, ma non è chiaro se l'onere progettuale di questi elementi sia giustificato, solo perché stiamo già utilizzando proprietà parziali.