Кога се изпълнява? Изпълнява ли се за всеки обект, към който го прилагам, или само веднъж? Може ли да прави нещо или действията му са ограничени?
Кога се изпълнява конструкторът на потребителски атрибут?
Отговори (4)
Кога се изпълнява конструкторът? Опитайте го с проба:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Creating MyClass instance");
MyClass mc = new MyClass();
Console.WriteLine("Setting value in MyClass instance");
mc.Value = 1;
Console.WriteLine("Getting attributes for MyClass type");
object[] attributes = typeof(MyClass).GetCustomAttributes(true);
}
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute
{
public MyAttribute()
{
Console.WriteLine("Running constructor");
}
}
[MyAttribute]
class MyClass
{
public int Value { get; set; }
}
И какъв е резултатът?
Creating MyClass instance
Setting value in MyClass instance
Getting attributes for MyClass type
Running constructor
И така, конструкторът на атрибути се изпълнява, когато започнем да изследваме атрибута. Имайте предвид, че атрибутът се извлича от типа, а не от екземпляра на типа.
CustomAttribute
данните за атрибут, който винаги е свързан с определени Type
, дефинирайте персонализиран TypeDelegator
(или йерархия от TypeDelegator
класове, която е паралелна на йерархията на наследяване на вашите действителни класове). TypeDelegator
е разумно място за поставяне на персонализирана Type
информация, тъй като наследява от Type
. Това разширява съответните екземпляри на CLR Type
с допълнителни метаданни за време на изпълнение на поле, метод и свойство въз основа на вашите потребителски данни за атрибути. След това вашето приложение надгражда всеки Type
до кеширан TypeDelegator
само веднъж и след това винаги се занимава с последния.
- person Glenn Slayden; 18.08.2020
Конструкторът се изпълнява всеки път, когато се извика GetCustomAttributes
или всеки път, когато друг код извиква конструктора директно (не че има основателна причина да се направи това, но не е и невъзможно).
Имайте предвид, че поне в .NET 4.0 екземплярите на атрибута не са кеширани; нов екземпляр се конструира всеки път, когато се извика GetCustomAttributes
:
[Test]
class Program
{
public static int SomeValue;
[Test]
public static void Main(string[] args)
{
var method = typeof(Program).GetMethod("Main");
var type = typeof(Program);
SomeValue = 1;
Console.WriteLine(method.GetCustomAttributes(false)
.OfType<TestAttribute>().First().SomeValue);
// prints "1"
SomeValue = 2;
Console.WriteLine(method.GetCustomAttributes(false)
.OfType<TestAttribute>().First().SomeValue);
// prints "2"
SomeValue = 3;
Console.WriteLine(type.GetCustomAttributes(false)
.OfType<TestAttribute>().First().SomeValue);
// prints "3"
SomeValue = 4;
Console.WriteLine(type.GetCustomAttributes(false)
.OfType<TestAttribute>().First().SomeValue);
// prints "4"
Console.ReadLine();
}
}
[AttributeUsage(AttributeTargets.All)]
class TestAttribute : Attribute
{
public int SomeValue { get; private set; }
public TestAttribute()
{
SomeValue = Program.SomeValue;
}
}
Не е най-добрата идея атрибутите да се държат по този начин, разбира се. Най-малкото имайте предвид, че GetCustomAttributes
не е документирано да се държи по този начин; всъщност какво се случва в горната програма не е посочено в документацията.
Задайте точка на прекъсване на дебъгер вътре в конструктор на атрибути и напишете някакъв код за отразяване, който чете тези атрибути. Ще забележите, че атрибутните обекти няма да бъдат създадени, докато не бъдат върнати от API за отражение. Атрибутите са за клас. Те са част от метаданните.
Разгледайте това:
Program.cs
using System;
using System.Linq;
[My(15)]
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Program started");
var ats =
from a in typeof(Program).GetCustomAttributes(typeof(MyAttribute), true)
let a2 = a as MyAttribute
where a2 != null
select a2;
foreach(var a in ats)
Console.WriteLine(a.Value);
Console.WriteLine("Program ended");
Console.ReadLine();
}
}
MyAttribute.cs
using System;
[AttributeUsage(validOn : AttributeTargets.Class)]
public class MyAttribute : Attribute
{
public MyAttribute(int x)
{
Console.WriteLine("MyAttribute created with {0}.", x);
Value = x;
}
public int Value { get; private set; }
}
Резултат
Program started
MyAttribute created with 15.
15
Program ended
Но не се притеснявайте за производителността на конструкторите на атрибути. Те са най-бързата част от размисъла :-P
Метаданните в изпълнимия файл или DLL се съхраняват:
- Токен за метаданни, указващ конструктора за извикване
- Аргументите
Когато стигна до този раздел от моята реализация на CLI, планирам да извикам отложено конструктора при първото извикване на GetCustomAttributes()
за ICustomAttributeProvider
. Ако се поиска конкретен тип атрибут, ще конструирам само онези, които са необходими за връщане на този тип.