C# Language
attributi
Ricerca…
Creare un attributo personalizzato
//1) All attributes should be inherited from System.Attribute
//2) You can customize your attribute usage (e.g. place restrictions) by using System.AttributeUsage Attribute
//3) You can use this attribute only via reflection in the way it is supposed to be used
//4) MethodMetadataAttribute is just a name. You can use it without "Attribute" postfix - e.g. [MethodMetadata("This text could be retrieved via reflection")].
//5) You can overload an attribute constructors
[System.AttributeUsage(System.AttributeTargets.Method | System.AttributeTargets.Class)]
public class MethodMetadataAttribute : System.Attribute
{
//this is custom field given just for an example
//you can create attribute without any fields
//even an empty attribute can be used - as marker
public string Text { get; set; }
//this constructor could be used as [MethodMetadata]
public MethodMetadataAttribute ()
{
}
//This constructor could be used as [MethodMetadata("String")]
public MethodMetadataAttribute (string text)
{
Text = text;
}
}
Utilizzando un attributo
[StackDemo(Text = "Hello, World!")]
public class MyClass
{
[StackDemo("Hello, World!")]
static void MyMethod()
{
}
}
Leggere un attributo
Metodo GetCustomAttributes
restituisce una serie di attributi personalizzati applicati al membro. Dopo aver recuperato questo array è possibile cercare uno o più attributi specifici.
var attribute = typeof(MyClass).GetCustomAttributes().OfType<MyCustomAttribute>().Single();
Oppure scorrere attraverso di loro
foreach(var attribute in typeof(MyClass).GetCustomAttributes()) {
Console.WriteLine(attribute.GetType());
}
GetCustomAttribute
metodo di estensione GetCustomAttribute
di System.Reflection.CustomAttributeExtensions
recupera un attributo personalizzato di un tipo specificato, può essere applicato a qualsiasi MemberInfo
.
var attribute = (MyCustomAttribute) typeof(MyClass).GetCustomAttribute(typeof(MyCustomAttribute));
GetCustomAttribute
ha anche una firma generica per specificare il tipo di attributo da cercare.
var attribute = typeof(MyClass).GetCustomAttribute<MyCustomAttribute>();
L'argomento Boolean inherit
può essere passato ad entrambi questi metodi. Se questo valore è impostato su true
gli antenati dell'elemento dovrebbero essere esaminati.
DebuggerDisplay Attribute
Aggiungere l'attributo DebuggerDisplay
cambierà il modo in cui il debugger visualizza la classe quando viene posizionata al passaggio del mouse.
Le espressioni racchiuse in {}
verranno valutate dal debugger. Può trattarsi di una proprietà semplice come nell'esempio seguente o in una logica più complessa.
[DebuggerDisplay("{StringProperty} - {IntProperty}")]
public class AnObject
{
public int ObjectId { get; set; }
public string StringProperty { get; set; }
public int IntProperty { get; set; }
}
Aggiungendo ,nq
prima della parentesi di chiusura rimuove le virgolette quando si stampa una stringa.
[DebuggerDisplay("{StringProperty,nq} - {IntProperty}")]
Anche se le espressioni generali sono consentite in {}
non sono raccomandate. L'attributo DebuggerDisplay
verrà scritto nei metadati dell'assieme sotto forma di stringa. Le espressioni in {}
non vengono verificate per la validità. Quindi un attributo DebuggerDisplay
contenente una logica più complessa di una semplice aritmetica potrebbe funzionare bene in C #, ma la stessa espressione valutata in VB.NET probabilmente non sarà sintatticamente valida e produrrà un errore durante il debug.
Un modo per rendere DebuggerDisplay
più indipendente dalla lingua è scrivere l'espressione in un metodo o in una proprietà e chiamarla invece.
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
public class AnObject
{
public int ObjectId { get; set; }
public string StringProperty { get; set; }
public int IntProperty { get; set; }
private string DebuggerDisplay()
{
return $"{StringProperty} - {IntProperty}"";
}
}
Si potrebbe desiderare che DebuggerDisplay
restituisca tutte o solo alcune proprietà e durante il debug e l'ispezione del tipo dell'oggetto.
L'esempio seguente circonda anche il metodo helper con #if DEBUG
dato che DebuggerDisplay
viene utilizzato negli ambienti di debug.
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
public class AnObject
{
public int ObjectId { get; set; }
public string StringProperty { get; set; }
public int IntProperty { get; set; }
#if DEBUG
private string DebuggerDisplay()
{
return
$"ObjectId:{this.ObjectId}, StringProperty:{this.StringProperty}, Type:{this.GetType()}";
}
#endif
}
Attributi di informazioni sul chiamante
Gli attributi di informazioni sul chiamante possono essere utilizzati per trasmettere informazioni sull'invocatore al metodo richiamato. La dichiarazione assomiglia a questo:
using System.Runtime.CompilerServices;
public void LogException(Exception ex,
[CallerMemberName]string callerMemberName = "",
[CallerLineNumber]int callerLineNumber = 0,
[CallerFilePath]string callerFilePath = "")
{
//perform logging
}
E l'invocazione assomiglia a questo:
public void Save(DBContext context)
{
try
{
context.SaveChanges();
}
catch (Exception ex)
{
LogException(ex);
}
}
Si noti che solo il primo parametro viene passato esplicitamente al metodo LogException
mentre il resto di essi verrà fornito in fase di compilazione con i valori pertinenti.
Il parametro callerMemberName
riceverà il valore "Save"
, il nome del metodo di chiamata.
Il parametro callerLineNumber
riceverà il numero di qualsiasi riga su LogException
è scritta la chiamata del metodo LogException
.
E il parametro 'callerFilePath' riceverà il percorso completo del file in cui è stato dichiarato il metodo Save
.
Lettura di un attributo dall'interfaccia
Non esiste un modo semplice per ottenere attributi da un'interfaccia, poiché le classi non ereditano gli attributi da un'interfaccia. Ogni volta che si implementa un'interfaccia o si sovrascrivono i membri in una classe derivata, è necessario dichiarare nuovamente gli attributi. Quindi nell'esempio seguente l'output sarebbe True
in tutti e tre i casi.
using System;
using System.Linq;
using System.Reflection;
namespace InterfaceAttributesDemo {
[AttributeUsage(AttributeTargets.Interface, Inherited = true)]
class MyCustomAttribute : Attribute {
public string Text { get; set; }
}
[MyCustomAttribute(Text = "Hello from interface attribute")]
interface IMyClass {
void MyMethod();
}
class MyClass : IMyClass {
public void MyMethod() { }
}
public class Program {
public static void Main(string[] args) {
GetInterfaceAttributeDemo();
}
private static void GetInterfaceAttributeDemo() {
var attribute1 = (MyCustomAttribute) typeof(MyClass).GetCustomAttribute(typeof(MyCustomAttribute), true);
Console.WriteLine(attribute1 == null); // True
var attribute2 = typeof(MyClass).GetCustomAttributes(true).OfType<MyCustomAttribute>().SingleOrDefault();
Console.WriteLine(attribute2 == null); // True
var attribute3 = typeof(MyClass).GetCustomAttribute<MyCustomAttribute>(true);
Console.WriteLine(attribute3 == null); // True
}
}
}
Un modo per recuperare gli attributi dell'interfaccia è cercarli attraverso tutte le interfacce implementate da una classe.
var attribute = typeof(MyClass).GetInterfaces().SelectMany(x => x.GetCustomAttributes().OfType<MyCustomAttribute>()).SingleOrDefault();
Console.WriteLine(attribute == null); // False
Console.WriteLine(attribute.Text); // Hello from interface attribute
Attributo obsoleto
System.Obsolete è un attributo che viene utilizzato per contrassegnare un tipo o un membro che ha una versione migliore e pertanto non deve essere utilizzato.
[Obsolete("This class is obsolete. Use SomeOtherClass instead.")]
class SomeClass
{
//
}
Nel caso in cui la classe sopra sia usata, il compilatore darà l'avvertimento "Questa classe è obsoleta. Usa SomeOtherClass invece."