IT俱乐部 ASP.NET .NET Core 特性(Attribute)底层原理解析

.NET Core 特性(Attribute)底层原理解析

Attribute的使用场景

Attribute不仅仅局限于C#中,在整个.NET框架中都提供了非常大的拓展点,任何地方都有Attribute的影子

  • 编译器层
    比如 Obsolete,Conditional
  • C#层
    GET,POST,Max,Range,Require
  • CLR VM层
    StructLayout,DllImport
  • JIT 层
    MethodImpl

Attribute在C#中的调用

举个常用的例子,读取枚举上的自定义特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public enum Test
{
    [EnumDescription("hhhhhh")]
    None = 0,
    [EnumDescription("xxxxxx")]
    Done =1
}
private static IEnumerable GetEnumDescriptions(this Enum e)
{
    IEnumerable result = null;
    var type = e.GetType();
    var fieldInfo = type.GetField(e.ToString());
    var attr = fieldInfo?.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);
    if (attr?.Length > 0)
    {
        result = attr.Cast().Select(x => x.Description);
    }
    return result ?? Enumerable.Empty();
}

可以看到,Attribute底层在C#中实现依旧是依赖反射,所以为什么说Attribute是写给代码看的注释,因此对反射的优化思路也可以用在Attribute中。
比如在代码中,使用Dictionary缓存结果集。避免过多调用反射造成的性能问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static IEnumerable GetEnumDescriptionsCache(this Enum e)
{
    var key = $"{e.GetType().Name}_{e.ToString()}";
    if (_enumMap.ContainsKey(key))
    {
        return _enumMap[key];
    }
    else
    {
        var result = GetEnumDescriptions(e);
        _enumMap.TryAdd(key, result);
        return result;
    }
}

循环100000次造成的性能差距还是很明显的

Newtonsoft.Json对Attrubute的使用

以JsonConverter为蓝本举例说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Person
{
    [JsonConverter(typeof(DateTimeConverter))]
    public DateTime CreateTime { get; set; }
}
public class DateTimeConverter : JsonConverter
{
    public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.Value == null)
            return DateTime.MinValue;
        if (DateTime.TryParse(reader.Value.ToString(), out DateTime result))
            return result;
        return DateTime.MinValue;
    }
    public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
    {
        writer.WriteValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
    }
}

定义了一个Attribute:JsonConverter.其底层调用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[RequiresUnreferencedCode(MiscellaneousUtils.TrimWarning)]
[RequiresDynamicCode(MiscellaneousUtils.AotWarning)]
public static JsonConverter? GetJsonConverter(object attributeProvider)
{
    // 底层还是调用Reflection,为了性能,也缓存了对象元数据。
    JsonConverterAttribute? converterAttribute = GetCachedAttribute(attributeProvider);
    if (converterAttribute != null)
    {
        Func<object data-origwidth="" data-origheight="" style="width: 1264px;"> creator = CreatorCache.Instance.Get(converterAttribute.ConverterType);
        if (creator != null)
        {
            return (JsonConverter)creator(converterAttribute.ConverterParameters);
        }
    }
    return null;
}</object>

https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs

Attribute在CLR上的调用

1
2
3
4
5
public class NativeMethods
{
    [DllImport("xxxxx", EntryPoint = "add", CallingConvention = CallingConvention.Cdecl)]
    public extern static int ManagedAdd(int a, int b);
}

在CLR中,同样用来调用 C/C++ 的导出函数。有兴趣的朋友可以使用windbg查看线程调用栈。以及在MetaData中有一张ImplMap表,存储着C#方法与C++函数的mapping关系

Attribute在JIT上的调用

1
2
3
4
5
6
7
8
9
public class Person
{
    public int id { get; set; } = 0;
    [MethodImpl(MethodImplOptions.Synchronized)]
    public void SyncMethod()
    {
        id++;
    }
}

JIT会自动为该Attribute注入同步代码

其本质就是注入lock同步块代码,只是颗粒度在整个方法上。相对比较大

结论

Attrubute在C#层面,底层使用反射。因此使用自定义Attribute时,酌情使用缓存来提高性能

到此这篇关于.NET Core 特性(Attribute)底层原理浅谈的文章就介绍到这了,更多相关.NET Core 底层原理内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/code/asp-net/14136.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部