synopsis
Without further ado, the rotten information is simply thatComments for the code to see
Attribute usage scenarios
Attributes are not just limited to C#, they provide very large expansion points throughout the .NET Framework, and there are Attributes everywhere!
- compiler layer
For example, Obsolete, Conditional - C# layer
GET,POST,Max,Range,Require - CLR VM Layer
StructLayout,DllImport - JIT layer
MethodImpl
Attribute Calls in C#
As a common example, read custom features on enumerations.
public enum Test
{
[EnumDescription("hhhhhh")]
None = 0,
[EnumDescription("xxxxxx")]
Done =1
}
private static IEnumerable<string> GetEnumDescriptions(this Enum e)
{
IEnumerable<string> result = null;
var type = ();
var fieldInfo = (());
var attr = fieldInfo?.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);
if (attr?.Length > 0)
{
result = <EnumDescriptionAttribute>().Select(x => );
}
return result ?? <string>();
}
As you can see, the underlying Attribute implementation in C# still relies on reflection, which is why Attributes areComments written to the code, so the optimization ideas for reflection can also be used in Attribute.
For example, in your code, use Dictionary to cache the result set. Avoid performance problems caused by too many calls to reflection.
private static IEnumerable<string> GetEnumDescriptionsCache(this Enum e)
{
var key = $"{().Name}_{()}";
if (_enumMap.ContainsKey(key))
{
return _enumMap[key];
}
else
{
var result = GetEnumDescriptions(e);
_enumMap.TryAdd(key, result);
return result;
}
}
The performance difference caused by 100,000 cycles is still noticeable
Use of Attrubute
JsonConverter as a blueprint for an example.
public class Person
{
[JsonConverter(typeof(DateTimeConverter))]
public DateTime CreateTime { get; set; }
}
public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if ( == null)
return ;
if (((), out DateTime result))
return result;
return ;
}
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
{
(("yyyy-MM-dd HH:mm:ss"));
}
}
Defines an Attribute: JsonConverter. the underlying call is as follows:
[RequiresUnreferencedCode()]
[RequiresDynamicCode()]
public static JsonConverter? GetJsonConverter(object attributeProvider)
{
// The bottom layer still calls theReflection,For performance,Object metadata is also cached。
JsonConverterAttribute? converterAttribute = GetCachedAttribute<JsonConverterAttribute>(attributeProvider);
if (converterAttribute != null)
{
Func<object[]?, object> creator = ();
if (creator != null)
{
return (JsonConverter)creator();
}
}
return null;
}
/JamesNK//blob/master/Src//Serialization/
Attribute Calls on the CLR
public class NativeMethods
{
[DllImport("xxxxx", EntryPoint = "add", CallingConvention = )]
public extern static int ManagedAdd(int a, int b);
}
In the CLR, the same is used to call C/C++ exported functions. Those interested can use windbg to view the thread call stack. As well as in MetaData there is an ImplMap table that stores the mapping relationship between C# methods and C++ functions
Attribute invocation on JITs
public class Person
{
public int id { get; set; } = 0;
[MethodImpl()]
public void SyncMethod()
{
id++;
}
}
The JIT automatically injects synchronization code for this Attribute
The essence of this is the injection of lock synchronization block code, just granularity over the entire method. Relatively large
reach a verdict
Attrubute is at the C# level and uses reflection at the bottom. Therefore when using a custom Attribute, use caching as appropriate to improve performance