Dela via


Allmänna attribut

Not

Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.

Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader samlas in i de relevanta LDM-anteckningar (Language Design Meeting).

Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.

Championfråga: https://github.com/dotnet/csharplang/issues/124

Sammanfattning

När generiska objekt introducerades i C# 2.0 tilläts inte attributklasser att delta. Vi kan göra språket mer komponerbart genom att snarare lossa den här begränsningen. .NET Core-körningen har lagt till stöd för generiska attribut. Allt som saknas är stöd för generiska attribut i kompilatorn.

Motivation

För närvarande kan attributförfattare ta en System.Type som en parameter och låta användare skicka ett typeof uttryck för att tillhandahålla attributet med de typer som behövs. Men utanför analysverktygen finns det inget sätt för en attributförfattare att begränsa vilka typer som tillåts skickas till ett attribut via typeof. Om attribut kan vara generiska kan attributförfattare använda det befintliga systemet av typparameterbegränsningar för att uttrycka kraven för de typer som de tar som indata.

Detaljerad design

Följande avsnitt ändras: §15.2.4.2

Den direkta basklassen för en klasstyp får inte vara någon av följande typer: System.Array, System.Delegate, System.MulticastDelegate, System.Enum eller System.ValueType. Dessutom kan en allmän klassdeklaration inte använda System.Attribute som en direkt eller indirekt basklass.

En viktig anmärkning är att följande avsnitt i specifikationen inte påverkas när du refererar till användningspunkten för ett attribut, dvs. i en attributlista: Typparametrar - §8.5.

En typparameter kan inte användas någonstans inom ett attribut.

Det innebär att när ett generiskt attribut används måste dess konstruktion vara helt "stängd", dvs. inte innehålla några typparametrar, vilket innebär att följande fortfarande är otillåtet:

using System;
using System.Collections.Generic;

public class Attr<T1> : Attribute { }

public class Program<T2>
{
    [Attr<T2>] // error
    [Attr<List<T2>>] // error
    void M() { }
}

När ett generiskt attribut används i en attributlista har dess typargument samma begränsningar som typeof har på argumentet. Till exempel är [Attr<dynamic>] ett fel. Det beror på att "attributberoende" typer som dynamic, List<string?>, nintoch så vidare inte kan representeras fullt ut i den slutliga IL:n för ett attributtypargument, eftersom det inte finns någon symbol att "koppla" DynamicAttribute eller annat välkänt attribut till.

Nackdelar

Att ta bort begränsningen, resonera kring konsekvenserna och lägga till lämpliga tester är arbete.

Alternativ

Attributförfattare som vill att användare ska kunna identifiera kraven för de typer som de tillhandahåller till attribut måste skriva analysverktyg och vägleda sina användare att använda analysverktygen i sina versioner.

Olösta frågor

  • [x] Vad betyder AllowMultiple = false för ett generiskt attribut? Om vi har [Attr<string>] och [Attr<object>] båda används på en symbol, betyder det att "flera" av attributet används?
    • För tillfället är vi benägna att ta den mer restriktiva vägen här och överväga attributklassens ursprungliga definition när vi bestämmer om flera av den har tillämpats. Med andra ord är [Attr<string>] och [Attr<object>] som tillämpas tillsammans inte kompatibla med AllowMultiple = false.

Designa möten