https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/attributes

# 继承 Attribute

1
2
3
4
public class MySpecialAttribute : Attribute
{

}

With the above, I can now use [MySpecial] or [MySpecialAttribute] as an attribute elsewhere in the code base.

1
2
3
[MySpecial]
public class SomeOtherClass{
}

Attributes 是在编译时进行的,其构造函数被限制只允许部分数据类型被传入,否则会编译无法通过 比如:

1
2
3
4
5
6
7
8
9
10
public class GotchaAttribute : Attribute
{
public GotchaAttribute(Foo myClass, string str) {
}
}

[Gotcha(new Foo(), "test")] // 编译将无法通过
public class AttributeFail
{
}

# How to restrict attribute usage

特性可以被用于如下字段
- Assembly - Class - Constructor - Delegate - Enum - Event - Field - GenericParameter - Interface - Method - Module - Parameter - Property - ReturnValue - Struct 如果你想限制到特定类,你需要在 Attribute 上添加限制

1
2
3
4
[AttributeUsage(AttributeTargets.Class  AttributeTargets.Struct)]
public class MyAttributeForClassAndStructOnly : Attribute{

}

# 如何使用 Attribute 连接 Code 里的元素

使用反射

1
2
TypeInfo typeInfo = typeof(MyClass).GetTypeInfo();
Console.WriteLine("The assembly qualified name of MyClass is" + typeInfo.AssemblyQualifiedName);

That will print out something like:

1
The assembly qualified name of MyClass is ConsoleApplication.MyClass, attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

一旦你有了 TypeInfo (或者 MemberInfo/FieldInfo) Object, 你就可以使用 GetCustomAttributes 方法,他将会返回 Attribute 对象

1
2
3
4
var attrs = typeInfo.GetCustomAttributes();
foreach(var attr in attrs){
Console.WriteLine("Attribute on MyClass: " + attr.GetType().Name);
}

# 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpStudy
{
class Program
{
static void Main(string[] args)
{
LoginRoute lr = new LoginRoute();
if (lr.GetType().IsDefined(typeof(MyAttribute),false))
{
Console.WriteLine("定义了Router");
object[] objs =lr.GetType().GetCustomAttributes(false);
foreach(var obj in objs)
{
MyAttribute ma = obj as MyAttribute;
Console.WriteLine(ma.route + " " + ma.action);
}
}


Console.Read();
}
}

public class MyAttribute : Attribute {
public string route;
public string action;
public MyAttribute(string route,string action)
{
this.route = route;
this.action = action;
Console.WriteLine("MyAttribute执行");
}
}

[My("user","Login")]
public class LoginRoute
{
public LoginRoute()
{
Console.WriteLine("LoginRoute执行");
}
}

}

上述示例表示 Login 在特性之前运行,所以 C# 的 attribute 和装饰器模式还是不同的

# 简单封装

加缓存的 Attribute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CSharpStudy
{
public static class CustomAttributeExtensions
{
/// <summary>
/// Cache Data
/// </summary>
private static readonly ConcurrentDictionary<string, object> Cache = new ConcurrentDictionary<string, object>();

/// <summary>
/// 获取CustomAttribute Value
/// </summary>
/// <typeparam name="TAttribute">Attribute的子类型</typeparam>
/// <typeparam name="TReturn">TReturn的子类型</typeparam>
/// <param name="sourceType">取Attribute具体哪个属性值的匿名函数</param>
/// <param name="attributeValueAction">返回Attribute的值,没有则返回null</param>
/// <returns></returns>
public static TReturn GetCustomAttributeValue<TAttribute,TReturn>(this Type sourceType,Func<TAttribute,TReturn> attributeValueAction,string propertyName)
where TAttribute:Attribute
{
return _getAttributeValue(sourceType, attributeValueAction, propertyName);
}

public static TReturn GetCustomAttributeValue<TAttribute, TReturn>(this Type sourceType, Func<TAttribute, TReturn> attributeValueAction)
where TAttribute : Attribute
{
return _getAttributeValue(sourceType, attributeValueAction, null);
}

#region private method
private static TReturn _getAttributeValue<TAttribute,TReturn>(Type sourceType,Func<TAttribute,TReturn> attributeFunc,string propertyName)
where TAttribute : Attribute
{
var cacheKey = BuildKey<TAttribute>(sourceType, propertyName);
var value = Cache.GetOrAdd(cacheKey, k => GetValue(sourceType, attributeFunc, propertyName));
if (value is TReturn) return (TReturn)Cache[cacheKey];
return default(TReturn);
}

/// <summary>
/// 获取类名
/// </summary>
/// <typeparam name="TAttribute"></typeparam>
/// <param name="type"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
private static string BuildKey<TAttribute>(Type type,string propertyName) where TAttribute : Attribute
{
var attributeName = typeof(TAttribute).FullName;
if (string.IsNullOrEmpty(propertyName))
{
return type.FullName + "." + attributeName;
}

return type.FullName + "." + propertyName + "." + attributeName;
}

private static TReturn GetValue<TAttribute,TReturn>(this Type type,Func<TAttribute,TReturn> attributeValueAction,string name)
where TAttribute :Attribute
{
TAttribute attribute = default(TAttribute);
if (string.IsNullOrEmpty(name))
{
attribute = type.GetCustomAttribute<TAttribute>(false);
}
else
{
var propertyInfo = type.GetProperty(name);
if (propertyInfo != null)
{
attribute = propertyInfo.GetCustomAttribute<TAttribute>(false);
}
else
{
var fieldInfo = type.GetField(name);
if (fieldInfo != null)
{
attribute = fieldInfo.GetCustomAttribute<TAttribute>(false);
}
}
}

return attribute == null ? default(TReturn) : attributeValueAction(attribute);
}

#endregion

}
}

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpStudy
{
class Program
{
static void Main(string[] args)
{
LoginRoute lr = new LoginRoute();
if (lr.GetType().IsDefined(typeof(MyAttribute),false))
{
Console.WriteLine("定义了Router");
object[] objs =lr.GetType().GetCustomAttributes(false);
foreach(var obj in objs)
{
MyAttribute ma = obj as MyAttribute;
Console.WriteLine(ma.route + " " + ma.action);
}
}

var routerInfo = lr.GetType().GetCustomAttributeValue<MyAttribute, string>(x => x.route+x.action);
//上面那个框架会调用string的缓存,所以这里需要处理下
var actionInfo = lr.GetType().GetCustomAttributeValue<MyAttribute, string>(x => x.action);

Console.WriteLine(routerInfo + " " + actionInfo);

Console.Read();
}
}

public class MyAttribute : Attribute {
public string route;
public string action;
public MyAttribute(string route,string action)
{
this.route = route;
this.action = action;
Console.WriteLine("MyAttribute执行");
}
}

[My("user","Login")]
public class LoginRoute
{
public LoginRoute()
{
Console.WriteLine("LoginRoute执行");
}
}

}