Perché C # proibisce i tipi di attributi generici?


510

Ciò provoca un'eccezione durante la compilazione:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

Mi rendo conto che C # non supporta gli attributi generici. Tuttavia, dopo molto googling, non riesco a trovare il motivo.

Qualcuno sa perché i tipi generici non possono derivare Attribute? Qualche teoria?


17
Potresti fare [Convalida (typeof (stringa)] - Sono d'accordo che i generici sarebbero più belli ...
ConsultUtah

20
Anche se questa è un'aggiunta molto recente a questa domanda, è triste che non solo gli attributi stessi ma anche le classi di attributi astratte (che ovviamente non possono essere istanziate come attributi) non siano ammesse, in questo modo: abstract class Base<T>: Attribute {}che potrebbero essere utilizzate per creare non- classi derivate generiche come questa:class Concrete: Base<MyType> {}
Lucero,

88
Ho voglia di attributi generici e attributi che accettano lambdas. Immagina cose come [DependsOnProperty<Foo>(f => f.Bar)]o [ForeignKey<Foo>(f => f.IdBar)]...
Jacek Gorgoń,

3
Ciò sarebbe estremamente utile in una situazione che ho appena incontrato; sarebbe bello creare un LinkedValueAttribute che accettasse un tipo generico e lo imponesse sul valore effettivo specificato. Potrei usarlo per enum per specificare il valore "predefinito" di un altro enum che dovrebbe essere usato se si sceglie questo valore enum. È possibile specificare più di questi attributi per tipi diversi e ottenere il valore di cui ho bisogno in base al tipo di cui ho bisogno. Posso impostarlo per usare Type e Object ma essere fortemente tipizzato sarebbe un grande vantaggio.
KeithS

10
Se non ti dispiace un po 'IL, questo sembra promettente .
Jarrod Dixon

Risposte:


358

Bene, non posso rispondere perché non è disponibile, ma posso confermare che non si tratta di un problema con la CLI. Le specifiche CLI non lo menzionano (per quanto posso vedere) e se si utilizza IL direttamente è possibile creare un attributo generico. La parte della specifica C # 3 che la vieta - la sezione 10.1.4 "Specifica di base della classe" non fornisce alcuna giustificazione.

Le specifiche annotate ECMA C # 2 non forniscono neppure informazioni utili, sebbene forniscano un esempio di ciò che non è consentito.

La mia copia delle specifiche C # 3 con annotazioni dovrebbe arrivare domani ... Vedrò se questo fornisce ulteriori informazioni. Ad ogni modo, è sicuramente una decisione linguistica piuttosto che runtime.

EDIT: Risposta di Eric Lippert (parafrasato): nessun motivo particolare, se non per evitare complessità sia nel linguaggio che nel compilatore per un caso d'uso che non aggiunge molto valore.


139
"tranne per evitare la complessità sia nel linguaggio che nel compilatore" ... e quello delle persone che ci danno co e
contraddizione

254
"caso d'uso che non aggiunge molto valore"? Questa è un'opinione soggettiva, potrebbe fornirmi molto valore!
Jon Kruger,

34
La cosa che mi preoccupa di più di non avere questa funzione, non è riuscire a fare cose come [PropertyReference (x => x.SomeProperty)]. Invece, hai bisogno di stringhe magiche e typeof (), che penso faccia schifo.
Asbjørn Ulsberg,

13
@ John: Penso che tu abbia ampiamente sottovalutato i costi di progettazione, specifica, implementazione e test di una nuova funzionalità linguistica.
Jon Skeet,

14
Voglio solo aggiungere a difesa di @ Timwi che questo non è l'unico posto in cui vengono discussi , e le 13K opinioni su questa domanda implicano un sano livello di interesse. Inoltre: grazie per aver ottenuto una risposta autorevole, Jon.
Jordan Grey,

84

Un attributo decora una classe in fase di compilazione, ma una classe generica non riceve le informazioni sul tipo finale fino al runtime. Poiché l'attributo può influire sulla compilazione, deve essere "completo" al momento della compilazione.

Vedi questo articolo MSDN per ulteriori informazioni.


3
L'articolo ribadisce che non sono possibili, ma senza motivo. Comprendo concettualmente la tua risposta. Conosci qualche altra documentazione ufficiale sull'argomento?
Bryan Watts,

2
L'articolo copre il fatto che IL contiene ancora segnaposto generici che vengono sostituiti con il tipo effettivo in fase di esecuzione. Il resto è stato dedotto da me ... :)
GalacticCowboy

1
Per quello che vale, VB applica lo stesso vincolo: "Le classi che sono generiche o contenute in un tipo generico non possono ereditare da una classe di attributi".
GalacticCowboy,

1
ECMA-334, sezione 14.16 dice "Sono richieste espressioni costanti nei contesti elencati di seguito e questo è indicato nella grammatica usando l'espressione costante. In questi contesti, si verifica un errore di compilazione se un'espressione non può essere valutata completamente durante la compilazione- tempo." Gli attributi sono nell'elenco.
GalacticCowboy,

4
Ciò sembra essere contraddetto da un'altra risposta che afferma che IL lo consentirà. ( Stackoverflow.com/a/294259/3195477 )
UuDdLrLrSs

22

Non so perché non sia permesso, ma questa è una possibile soluzione alternativa

[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
    public ClassDescriptionAttribute(Type KeyDataType)
    {
        _KeyDataType = KeyDataType;
    }

    public Type KeyDataType
    {
        get { return _KeyDataType; }
    }
    private Type _KeyDataType;
}


[ClassDescriptionAttribute(typeof(string))]
class Program
{
    ....
}

3
Sfortunatamente, quando si consuma l'attributo si perde la digitazione in fase di compilazione. Immagina che l'attributo crei qualcosa di tipo generico. Puoi aggirarlo, ma sarebbe bello; è una di quelle cose intuitive che sei sorpreso di non poter fare, come la varianza (al momento).
Bryan Watts,

14
Purtroppo, cercare di non farlo è il motivo per cui ho trovato questa domanda SO. Immagino che dovrò limitarmi a trattare con typeof. Sembra davvero una parolaccia una parola sporca dopo che i generici esistono da così tanto tempo.
Chris Marisic,

13

Questo non è veramente generico e devi ancora scrivere una specifica classe di attributi per tipo, ma potresti essere in grado di utilizzare un'interfaccia di base generica per codificare un po 'sulla difensiva, scrivere un codice minore di quanto altrimenti richiesto, ottenere benefici del polimorfismo ecc.

//an interface which means it can't have its own implementation. 
//You might need to use extension methods on this interface for that.
public interface ValidatesAttribute<T>
{
    T Value { get; } //or whatever that is
    bool IsValid { get; } //etc
}

public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string>
{
    //...
}
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int>
{
    //...
}

[ValidatesString]
public static class StringValidation
{

}
[ValidatesInt]
public static class IntValidation
{

}

8

Questa è un'ottima domanda Nella mia esperienza con gli attributi, credo che il vincolo è a posto, perché quando riflettendo su un attributo si creerebbe una condizione in cui si dovrà verificare la presenza di tutte le possibili permutazioni di tipo: typeof(Validates<string>), typeof(Validates<SomeCustomType>), ecc ...

Secondo me, se è necessaria una convalida personalizzata a seconda del tipo, un attributo potrebbe non essere l'approccio migliore.

Forse una classe di validazione che accetta un SomeCustomValidationDelegateo ISomeCustomValidatorcome parametro sarebbe un approccio migliore.


Sono d'accordo con te. Ho questa domanda da molto tempo e attualmente sto costruendo un sistema di validazione. Ho usato la mia attuale terminologia per porre la domanda, ma non ho intenzione di attuare un approccio basato su questo meccanismo.
Bryan Watts,

Mi sono imbattuto in questo mentre stavo lavorando a un progetto per lo stesso obiettivo: la convalida. Sto cercando di farlo in un modo che sia facile da analizzare sia automaticamente (cioè, potresti generare un rapporto che descrive la convalida nell'applicazione per la conferma) sia da un umano che visualizza il codice. Se non gli attributi, non sono sicuro di quale sarebbe la soluzione migliore ... Potrei ancora provare la progettazione degli attributi, ma dichiarare manualmente gli attributi specifici del tipo. È un po 'più di lavoro, ma l'obiettivo è l'affidabilità di conoscere le regole di convalida (e di essere in grado di riferire su di esse per la conferma).
bambams,

4
è possibile verificare con le definizioni di tipo generico (ovvero typeof (Validates <>)) ...
Melvyn

5

Questa non è attualmente una funzionalità del linguaggio C #, tuttavia si discute molto sul repository ufficiale del linguaggio C # .

Da alcune note sulla riunione :

Anche se questo funzionerebbe in linea di principio, nella maggior parte delle versioni del runtime sono presenti bug in modo che non funzionino correttamente (non è mai stato esercitato).

Abbiamo bisogno di un meccanismo per capire su quale runtime di destinazione funziona. Ne abbiamo bisogno per molte cose e al momento lo stiamo esaminando. Fino ad allora, non possiamo prenderlo.

Candidato per una versione C # importante, se riusciamo a farne un numero sufficiente di versioni di runtime.


1

La mia soluzione alternativa è qualcosa del genere:

public class DistinctType1IdValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type1> validator;

    public DistinctIdValidation()
    {
        validator = new DistinctValidator<Type1>(x=>x.Id);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

public class DistinctType2NameValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type2> validator;

    public DistinctType2NameValidation()
    {
        validator = new DistinctValidator<Type2>(x=>x.Name);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

...
[DataMember, DistinctType1IdValidation ]
public Type1[] Items { get; set; }

[DataMember, DistinctType2NameValidation ]
public Type2[] Items { get; set; }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.