Attribute作为一种标记在我们的.net中随处可见,比如DatContract,DatMember,Serializable等等,各种用途的标记。是的我们的代码更加简洁,对于Attribute用好了,可以很好的简化我们的开发,比如实现就是一种基于Attribute的标记编译时注入。在随笔中有的实例。

   在使用Attribute时候发现了些鲜为人知的特性:

1:利用GetCustomAttributes传入的Attribute返回得到包括派生类。

2:GetCustomAttributes每次返回的对象都是经过发射出来的没有缓存。

   1:GetCustomAttributes传入的Attribute返回得到包括派生类:

       这里将采用一个测试类来验证:

[AttributeUsage(AttributeTargets.Class)] 
 
public 
class TestImplementsAttribute : Attribute 
  { 
     
public 
string Name 
      {
get;
set; } 
  }
 
private 
static 
void TestMutilpeImplements() 
   
var type =
typeof(Program); 
   
var attrs = type.GetCustomAttributes(
typeof(TestImplementsAttribute),
false); 
    Console.WriteLine(
string.Format(
"
TestImplementsAttribute:({0})
",attrs.Length)); 
   
foreach (
var item
in attrs) 
    { 
        Console.WriteLine(
"
 
" + item.GetType().FullName); 
    } 
    attrs = type.GetCustomAttributes(
typeof(SerializableAttribute),
false); 
    Console.WriteLine(
string.Format(
"
SerializableAttribute:({0})
", attrs.Length)); 
   
foreach (
var item
in attrs) 
    { 
        Console.WriteLine(
"
 
" + item.GetType().FullName); 
    } 
    attrs = type.GetCustomAttributes(
typeof(Attribute),
false); 
    Console.WriteLine(
string.Format(
"
(base type)Attribute:({0})
", attrs.Length)); 
   
foreach (
var item
in attrs) 
    { 
        Console.WriteLine(
"
 
" + item.GetType().FullName); 
    } 
}

输出为: 

这里我们可以很清晰的看见当传入Attribute类型时候返回包含了SerializableAttribute和TestImplementsAttribute两个。

2:GetCustomAttributes每次返回的对象都是经过发射出来的没有缓存:

测试代码可以看出来,不是同一个地址引用:

private 
static 
void TestAttributeActiver() 
      { 
         
var type =
typeof(Program); 
         
var attr1 = type.GetCustomAttributes(
typeof(TestImplementsAttribute),
false)[
0]; 
         
var attr2 = type.GetCustomAttributes(
typeof(TestImplementsAttribute),
false)[
0]; 
          Console.WriteLine(Object.ReferenceEquals(attr1, attr2));            
      }

输出值为false。 

我们在看看

.下面是 reflector的反编译结果(Attribute.GetCustomAttributes):

internal 
static 
unsafe 
object[] GetCustomAttributes(Module decoratedModule,
int decoratedMetadataToken,
int pcaCount, RuntimeType attributeFilterType,
bool mustBeInheritable, IList derivedAttributes)
{
   
if (decoratedModule.Assembly.ReflectionOnly)
    {
       
throw 
new InvalidOperationException(Environment.GetResourceString(
"
Arg_ReflectionOnlyCA
"));
    }
    MetadataImport metadataImport = decoratedModule.MetadataImport;
    CustomAttributeRecord[] customAttributeRecords = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken);
    Type elementType = (((attributeFilterType ==
null) || attributeFilterType.IsValueType) || attributeFilterType.ContainsGenericParameters) ?
typeof(
object) : attributeFilterType;
   
if ((attributeFilterType ==
null) && (customAttributeRecords.Length ==
0))
    {
       
return (Array.CreateInstance(elementType,
0)
as 
object[]);
    }
   
object[] attributes = Array.CreateInstance(elementType, customAttributeRecords.Length)
as 
object[];
   
int length =
0;
    SecurityContextFrame frame =
new SecurityContextFrame();
    frame.Push(decoratedModule.Assembly.InternalAssembly);
    Assembly lastAptcaOkAssembly =
null;
   
for (
int i =
0; i < customAttributeRecords.Length; i++)
    {
       
bool flag2;
       
bool flag3;
       
object obj2 =
null;
        CustomAttributeRecord caRecord = customAttributeRecords[i];
        RuntimeMethodHandle ctor =
new RuntimeMethodHandle();
        RuntimeType attributeType =
null;
       
int namedArgs =
0;
        IntPtr signature = caRecord.blob.Signature;
        IntPtr blobEnd = (IntPtr) (((
void*) signature) + caRecord.blob.Length);
       
if (FilterCustomAttributeRecord(caRecord, metadataImport,
ref lastAptcaOkAssembly, decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, attributes, derivedAttributes,
out attributeType,
out ctor,
out flag2,
out flag3))
        {
           
if (!ctor.IsNullHandle())
            {
                ctor.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken);
            }
            RuntimeConstructorInfo.CheckCanCreateInstance(attributeType, flag3);
           
if (flag2)
            {
                obj2 = CreateCaObject(decoratedModule, ctor,
ref signature, blobEnd,
out namedArgs);
            }
           
else
            {
                obj2 = attributeType.TypeHandle.CreateCaInstance(ctor);
               
if (Marshal.ReadInt16(signature) !=
1)
                {
                   
throw 
new CustomAttributeFormatException();
                }
                signature = (IntPtr) (((
void*) signature) +
2);
                namedArgs = Marshal.ReadInt16(signature);
                signature = (IntPtr) (((
void*) signature) +
2);
            }
           
for (
int j =
0; j < namedArgs; j++)
            {
               
string str;
               
bool flag4;
                Type type3;
               
object obj3;
                IntPtr ptr1 = caRecord.blob.Signature;
                GetPropertyOrFieldData(decoratedModule,
ref signature, blobEnd,
out str,
out flag4,
out type3,
out obj3);
               
try
                {
                   
if (flag4)
                    {
                       
if ((type3 ==
null) && (obj3 !=
null))
                        {
                            type3 = (obj3.GetType() ==
typeof(RuntimeType)) ?
typeof(Type) : obj3.GetType();
                        }
                        RuntimePropertyInfo property =
null;
                       
if (type3 ==
null)
                        {
                            property = attributeType.GetProperty(str)
as RuntimePropertyInfo;
                        }
                       
else
                        {
                            property = attributeType.GetProperty(str, type3, Type.EmptyTypes)
as RuntimePropertyInfo;
                        }
                        RuntimeMethodInfo setMethod = property.GetSetMethod(
true)
as RuntimeMethodInfo;
                       
if (setMethod.IsPublic)
                        {
                            setMethod.MethodHandle.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken);
                            setMethod.Invoke(obj2, BindingFlags.Default,
null,
new 
object[] { obj3 },
null,
true);
                        }
                    }
                   
else
                    {
                        (attributeType.GetField(str)
as RtFieldInfo).InternalSetValue(obj2, obj3, BindingFlags.Default, Type.DefaultBinder,
null,
false);
                    }
                }
               
catch (Exception exception)
                {
                   
throw 
new CustomAttributeFormatException(
string.Format(CultureInfo.CurrentUICulture, Environment.GetResourceString(flag4 ?
"
RFLCT.InvalidPropFail
" :
"
RFLCT.InvalidFieldFail
"),
new 
object[] { str }), exception);
                }
            }
           
if (!signature.Equals(blobEnd))
            {
               
throw 
new CustomAttributeFormatException();
            }
            attributes[length++] = obj2;
        }
    }
    frame.Pop();
   
if ((length == customAttributeRecords.Length) && (pcaCount ==
0))
    {
       
return attributes;
    }
   
if (length ==
0)
    {
        Array.CreateInstance(elementType,
0);
    }
   
object[] destinationArray = Array.CreateInstance(elementType, (
int) (length + pcaCount))
as 
object[];
    Array.Copy(attributes,
0, destinationArray,
0, length);
   
return destinationArray;
}

在这里我们可以见数组的创建等等。

   同时可以参见老赵前辈以前的关于Attribute反射的和。

   不知道为什么在Attribute参数的检查是在我们的编译时期,参数必须是常量表达式,却在这里需要每次反射。

   本篇随笔只是个人使用心得记录,请勿拍砖。