Domanda

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 tanto Google, non riesco a trovare il motivo.

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

È stato utile?

Soluzione

Beh, non posso rispondere perché non è disponibile, ma posso confermare che non si tratta di un problema alla 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 - sezione 10.1.4 & Quot; Specifica base di classe & Quot; non fornisce alcuna giustificazione.

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

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

MODIFICA: 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.

Altri suggerimenti

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 & Quot; complete & Quot; al momento della compilazione.

Vedi questo articolo MSDN per ulteriori informazioni.

Non so perché non sia consentito, 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
{
    ....
}

Questo non è veramente generico e devi ancora scrivere una specifica classe di attributo 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
{

}

Questa è un'ottima domanda. Nella mia esperienza con gli attributi, penso che il vincolo sia in atto perché, riflettendo su un attributo, creerebbe una condizione in cui dovresti verificare 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 SomeCustomValidationDelegate o un ISomeCustomValidator come parametro sarebbe un approccio migliore.

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; }

Questa non è attualmente una funzione del linguaggio C #, tuttavia si discute molto sul linguaggio ufficiale C # repo .

Da alcune note sulla riunione :

  

Anche se questo funzionerebbe in linea di principio, ci sono molti bug   versioni del runtime in modo che non funzionasse correttamente (lo era   mai esercitato).

     

Abbiamo bisogno di un meccanismo per capire su quale runtime di destinazione funziona. Noi   ne ho bisogno per molte cose e al momento lo stiamo osservando. Fino a   quindi, non possiamo prenderlo.

     

Candidato per una versione C # importante, se riusciamo a fare un numero sufficiente   delle versioni di runtime si occupano di esso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top