使用 CustomAttributes 与 GetCustomAttributes() 的优势

Advantage of using CustomAttributes vs GetCustomAttributes()

我今天注意到我的 .NET 4.5 项目的 System.Type 对象的智能感知中出现了一些新属性。其中有一个叫做 CustomAttributes.

我对此很感兴趣,因为我之前了解到 GetCustomAttributes 是最昂贵的反射调用之一(当然,DynamicInvoke 等等除外)。据我了解,每次调用 GetCustomAttributes 都会导致调用属性的构造函数(从而进行内存分配)。我经常求助于单独缓存自定义属性以避免在处理大量类型等时出现性能瓶颈。

所以,我编写了一个测试,看看 CustomAttributes 是否比 GetCustomAttributes 更高效:

static void Main(string[] args)
{
    var sw = Stopwatch.StartNew();

    Debug.WriteLine(typeof(Attributed).GetType());

    for (int i = 0; i < 10000; i++)
    {
        var attrs = typeof(Attributed)
            .CustomAttributes
            .Select(a => a.AttributeType)
            .ToList();
    }

    sw.Stop();
    Debug.WriteLine("Using .NET 4.5 CustomAttributes property: {0}", sw.Elapsed);

    sw = Stopwatch.StartNew();

    for (int i = 0; i < 10000; i++)
    {
        var attrs = typeof(Attributed)
            .GetCustomAttributes(true)
            .Select(a => a.GetType())
            .ToList();
    }

    sw.Stop();
    Debug.WriteLine("Using GetCustomAttributes method: {0}", sw.Elapsed);
}

经过一些测试类:

[Dummy]
[Dummy]
[Dummy]
[Dummy]
[Dummy]
[Dummy]
class Attributed
{
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
class DummyAttribute : Attribute
{
    public DummyAttribute()
    {
    }
}

结果令人惊讶:

System.RuntimeType
Using .NET 4.5 CustomAttributes property: 00:00:00.1351259
Using GetCustomAttributes method: 00:00:00.0803161

新的 CustomAttributes 属性 实际上比现有的 GetCustomAttributes 方法慢!

进一步调试,我发现没有调用属性构造函数来迭代 CustomAttributes(这是我预料到的,因为它看起来只是在读取元数据)。然而不知何故,它比调用构造函数的 GetCustomAttributes 慢。

我的问题

我个人认为使用新的 属性 更具可读性,但代价是性能降低 1.5 倍左右。

那么,使用 CustomAttributes 而不是 GetCustomAttributes() 有什么好处(如果有的话)?

我假设我们只是检查类中是否存在某种类型的属性...而不是在属性的实例上使用方法或属性。

您犯了一个传统的基准测试错误,许多 .NET 程序员认为反射很慢。比实际速度慢。反射很偷懒,不用的时候不用付费。这使得您的 first 测量包括将元数据页面错误放入 RAM 和设置类型信息反射缓存的所有成本。该成本不包括在第二次测量中,使 GetCustomAttributes() 看起来比实际更好。

始终在基准代码周围包含一个循环,运行 循环 10 次。您现在会看到 CustomAttributes 属性 实际上并不 那个 慢,我测量它(大约)0.083 对 0.063 秒,慢 ~30%。

需要在 .NET 4.5 中添加 CustomAttributes 属性 以支持 WinRT 的语言投影。您不能在 Store、Phone 或 PCL 项目中使用 GetCustomAttributes()。反射在 WinRT 中 非常 不同,这是基于 COM 的不可避免的副作用 api。实现代码足以让任何人的眼睛流血,但大纲是 属性 是在 C# 中实现的,而该方法是在 CLR 中实现的。 C# 代码需要做更多的工作来处理语言投影细节,因此不可避免地会变慢。

因此,请继续使用 GetCustomAttributes(),必要时使用 属性。应用您的常识,30% 的速度差异并不是妥协风格和可读性的过分理由。

一个重要的区别似乎是 CustomAttributes returns IEnumerable 而 GetCustomAttributes returns object[] 包含从 Attribute 派生的实例。

从 net6 开始,GetCustomAttributes 令人惊讶地检查属性相对于它所应用的类型的可访问性,而 CustomAttributes 则不会。当您在运行时生成程序集并将自己的内部属性应用于生成的类型时,可以观察到这一点。考虑程序中的以下程序集

program
| -- DefaultAssembly # assembly generated by C# compiler, loaded from dll on disk
|       MyInternalAttribute # type is declared in code and compiled to currently executing program
| -- ReflectionEmitDynamicAssembly # assembly created at runtime with AssemblyBuilder
|       RuntimeGeneratedType # type is generated at runtime and annotated with MyInternalAttribute

给定 System.Type 类型的变量 type 指向 RuntimeGeneratedType 并访问 type.GetCustomAttributes().Count() 将得到 0,但访问 type.CustomAttributes.Count() 将给你1。这可以通过使用众所周知的 InternalsVisibleTo 来避免,但您需要标记您的程序集。或者 IgnoresAccessChecksTo 可用于注释生成的程序集。

正如sjb-sjb和JL0PD的回答所暗示的那样,这两个东西给出的信息不一样,所以比较没有意义。

我推荐第三种方法,在 .NET 4.5 及更高版本下,即新的扩展方法。例如:

typeof(Attributed).GetCustomAttributes()                  // no bool 'inherit' on this overload

或:

typeof(Attributed).GetCustomAttributes<DummyAttribute>()  // gets only attributes of the specified type, and no need for casting

要在范围内使用这些扩展方法,您需要指令:

using System.Reflection;

the documentation