IsDefined的问题

在.NET 4.0(当然也包括4.0以前的版本)下,用反射判断某个方法是否运用了自定义Attribute时,可以通过调用MethodInfo的IsDefined()方法进行确认。当然,IsDefined()方法事实上定义在MethodInfo的父类MemberInfo中,但它仅仅被定义为抽象方法,真正的实现是在MethodInfo的子类DynamicMethod中。调用方式如下所示:

methodInfo.IsDefined(typeof(MyAttribute), false)

然而,在实际开发中,我发现该方法有一个问题。如果获得MethodInfo的方式是通过加载程序集,然后利用反射方式获得的MethodInfo对象,即使该方法运用了自定义Attribute,返回的结果仍然是false。例如,我们将需要判断的方法所在的类定义到一个单独的Project中,并编译为单独的dll文件,然后,利用Assembly的LoadFile()方式获得程序集:

  var assembly = Assembly.LoadFile(assemblyPath);
  var types = assembly.GetExportedTypes();
  types.ToList().ForEach(
      type =>
      {
          var flag =
              type.GetMethods().Where(methodInfo => !methodInfo.IsAbstract).Any(
                  methodInfo => methodInfo.IsDefined(typeof(MyAttribute), false));
          Console.WriteLine("Flag of IsDefined is: {0}", flag);
      }
  );

打印出来的值为false。

反之,如果不是通过加载程序集,而是直接通过typeof()获得的Type,并调用其下MethodInfo.IsDefined()方法,只要该方法被运用了指定的Attribute,返回的结果则为true。

分析原因,大约是获得Type的方式不同所造成的。Assembly类的GetExportedType()实现如下所示:

[SecuritySafeCritical]
public override Type[] GetExportedTypes()
{
    Type[] o = null;
    GetExportedTypes(this.GetNativeHandle(), JitHelpers.GetObjectHandleOnStack<Type[]>(ref o));
    return o;
}

注意,这里返回的Type[]事实上是通过引用方式传递给了JitHelpers的GetObjectHandleOnStack<Type[]>方法中:

[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), SecurityCritical]
internal static ObjectHandleOnStack GetObjectHandleOnStack<T>(ref T o) where T: class
{
    TypedReference reference = __makeref(o);
    return new ObjectHandleOnStack(reference.GetPointerOnStack());
}

这里将Type转换成了TypedReference。关键大约就是这里,可惜我无法找到typeof()的具体实现方式。代码追踪到这里,就无法判断这里发生的真实原因了。若要了解.NET底层机制的同学,可以告诉我。

若要解决反射方式无法通过IsDefined()判断的问题,可以调用MethodInfo的GetCustomAttribute()方法。例如:

private static bool IsAppliedWith(this MethodInfo methodInfo, Type attributeType, string attributeName) 
{
    return methodInfo.GetCustomAttributes(attributeType, false).ToString().Contains(attributeName);
}

无论是利用反射加载,还是使用typeof,采用这种方式判断方法是否运用了指定的Attribute,都是能够生效的。

posted @ 2011-10-11 09:34 张逸 阅读(1671) 评论(8) 编辑 收藏

 回复 引用 查看   
#1楼 2011-10-11 09:43 天行健 自强不息      
学习了
 回复 引用 查看   
#2楼 2011-10-11 10:12 地狱门神      
不要用LoadFile,那个似乎会把程序加载到Neither上下文。
Assembly.Load(AssemblyName.GetAssemblyName(FilePath))
这个可以把程序加载到当前程序的上下文。

不知道是不是这个原因导致的。

 回复 引用 查看   
#3楼 2011-10-11 10:45 gunnima      
根据我看的文章或书中基本很少用IsDefined方法来判断,准确说我就没看到过用这个方法!我也没有去深入研究为什么。因为反射这玩意用得少!
 回复 引用 查看   
#4楼[楼主] 2011-10-11 10:55 张逸      
写这篇博客,就是希望寻找答案。
 回复 引用 查看   
#5楼 2011-10-11 13:52 Virus-BeautyCode      
/*
 * Created by SharpDevelop.
 * User: Administrator
 * Date: 2011-10-10
 * Time: 10:20
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using NUnit.Framework;
using NUnit.Mocks;
using System.Reflection;
using System.Linq;

namespace UnitTest.ConsoleApp
{
    [AttributeUsage(AttributeTargets.Method)]
    public class CustomAttribute : Attribute
    {
    }

    public class CustomClass
    {
        [Custom]
        public void PublicMethod()
        { }
    }
	class Program
	{
        static string filename = Environment.CurrentDirectory + "\\UnitTest.ConsoleApp.exe";
		public static void Main(string[] args)
		{

            IsDefine();

			Console.Write("Press any key to continue . . . ");
			Console.ReadKey(true);
		}


        static bool IsDefine()
        {
            bool result = false;
            var assembly = Assembly.LoadFrom(filename );
           // var assembly = Assembly.LoadFile(filename);
           // var assembly = Assembly.Load(AssemblyName .GetAssemblyName( filename));

            var types = assembly.GetExportedTypes ();
            types.ToList().ForEach(
                type =>
                {
                    Console.WriteLine("Type is: {0}", type.Name);
                    var flag =
                        type.GetMethods().Any(methodInfo => methodInfo.IsDefined(typeof(CustomAttribute), false));
                    Console.WriteLine("Flag of IsDefined is: {0}", flag);
                    Console.WriteLine("-----------------------------------");
                }
            );

            return result;
        }

	}
}


上面的代码中我使用了3中方式加载程序集,但是结果都是一样的,都可以使用isdefine来判断是否使用了自定义attribute。

 回复 引用 查看   
#6楼 2011-10-11 14:03 henry      
只用过GetCustomAttributes,毕竟这个函除了知道有没有外,还能马上获取相关attribute操作。
顺便共享一个小函数
        public static T[] GetTypeAttributes<T>(Type type,bool inhert) where T : Attribute
        {
            T[] abs = (T[])type.GetCustomAttributes(typeof(T),inhert);
            return abs;
        }
        public static T[] GetPropertyAttributes<T>(PropertyInfo pi, bool inhert) where T : Attribute
        {
            T[] abs = (T[])pi.GetCustomAttributes(typeof(T), inhert);
            return abs;
        }
        public static T[] GetMethodAttributes<T>(MethodInfo mi, bool inhert) where T : Attribute
        {
            T[] abs = (T[])mi.GetCustomAttributes(typeof(T), inhert);
            return abs;
        }
        public static T[] GetParemeterAttributes<T>(ParameterInfo pi, bool inhert) where T : Attribute
        {
            T[] abs = (T[])pi.GetCustomAttributes(typeof(T), inhert);
            return abs;
        }


 回复 引用 查看   
#7楼 2011-10-11 14:22 通用权限组件源码      
顶一下,的确是技术牛人啊.
 回复 引用 查看   
#8楼 2011-10-14 10:28 DotNetProgramer      
学习,通过IsDefind()方法判断程序是否定义了Attribute。