When is a custom attribute's constructor run?

2019-01-03 06:57发布

When is it run? Does it run for each object to which I apply it, or just once? Can it do anything, or its actions are restricted?

4条回答
干净又极端
2楼-- · 2019-01-03 07:10

When is the constructor run? Try it out with a sample:

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

And what is the output?

Creating MyClass instance
Setting value in MyClass instance
Getting attributes for MyClass type
Running constructor

So, the attribute constructor is run when we start to examine the attribute. Note that the attribute is fetched from the type, not the instance of the type.

查看更多
放我归山
3楼-- · 2019-01-03 07:24

The constructor is run every time the GetCustomAttributes is invoked, or whenever some other code invokes the constructor directly (not that there's a good reason to do so, but it's not impossible either).

Note that at least in .NET 4.0, the attribute instances are not cached; a fresh instance is constructed every time GetCustomAttributes is called:

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

It is not the best idea to have attributes behave like this, of course. At the very least, note that GetCustomAttributes is not documented to behave like this; in fact, what happens in the above program is not specified in the documentation.

查看更多
The star\"
4楼-- · 2019-01-03 07:24

The metadata in the executable or DLL stores:

  • A metadata token indicating the constructor to call
  • The arguments

When I get to that section of my CLI implementation, I plan to lazy-call the constructor the first time GetCustomAttributes() is called for the ICustomAttributeProvider. If a particular attribute type is requested, I'll only construct the ones required to return that type.

查看更多
看我几分像从前
5楼-- · 2019-01-03 07:32

Set a debugger break-point inside an attribute constructor and write some reflection code that reads those attributes. You'll notice that the attribute objects won't be created until they are returned from the relfection API. Attributes are per class. They are part of the meta data.

Have a look at this:

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

Result

Program started
MyAttribute created with 15.
15
Program ended

But don't worry about the performance of attribute constructors. They are the fastest part of reflection :-P

查看更多
登录 后发表回答