65.9K
CodeProject 正在变化。 阅读更多。
Home

反射(.NET)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (4投票s)

2012年3月27日

CPOL

6分钟阅读

viewsIcon

13548

反射是.NET 非常好的特性之一,通过反射,我们可以获取类型的很多信息。当我提到类型时,我的意思是它

反射是 .NET 非常好的特性之一,通过反射,我们可以获取类型的很多信息。当我提到类型时,我的意思是它可以是 .NET 类型,如类、枚举、结构体等。通过反射,你可以获取类的所有方法、它接受的参数,甚至可以调用该类型的方法。我将向你介绍反射的基础知识,这将为你深入学习和在代码中使用它打下坚实的知识基础。这篇文章可能篇幅不长,但我的写作目的是提供关于反射的基本信息。所以,如果我想用一句话来总结反射的定义,那就是:“反射是一种机制,它为你提供了深入了解程序集、模块或类型的元数据的能力。”
那么,让我们从代码开始,这样你会对反射有一个很好的认识。为了简便起见,我将创建一个普通的类,包含一些方法和一个构造函数,一些 void 方法,以及一个接受两个 int 参数来展示它们相加的方法,另外还有一个主类,它将检索这些方法在运行时(无论它们是公共的还是私有的)的信息。因为反射提供了如此大的灵活性,所以你可以调用类的私有方法,这通常是你无法通过在代码中添加引用来直接实现的,是不是很有趣?是的,确实如此。
我创建了一个名为 AeroPlane 的类,其中包含一些公共方法、私有方法,有些接受参数,有些只是为了调用。为了更好地理解,请参考下面的代码。
class AeroPlane
    {
        public AeroPlane()
        {
            Console.WriteLine("AeroPlane Constructor invoked");
        }
        private void Model()
        {
            Console.WriteLine("Airbus A380");
        }
        private void Company()
        {
            Console.WriteLine("Airbus");
        }
        private void CompanyLoacation()
        {
            Console.WriteLine("United States");
        }
        public void PublicMethod()
        {
            Console.WriteLine("This is public Method");
        }
        private void DoSum(int a, int b)
        {
            Console.WriteLine("Addition of {0} and {1} is {2}", a, b, a + b);
        }
        private void Age(string name, int age)
        {
            Console.WriteLine("Hi {0} your age is : {1}", name, age);
        }
    }
正如你所见,我创建的大多数方法都是私有的。这没有什么特别的原因,只是我想要调用私有方法,满足一下我的好奇心。我有一些 void 方法,加上两个接受参数的方法。因此,我们将使用 .NET 反射在另一个类中检索这个类的所有详细信息。
让我们看看我的类结构,它将调用上述类的所有方法并检索它们的详细信息。首先,我们将看到代码,然后我们会看到我的类的输出。
namespace NamespaceReflectionInner
    {
        class TestReflection
        {
            Type _type = null;
            public TestReflection()
            {
                _type = Type.GetType("AeroPlane");
            }
            public void Proceed()
            {
                _type = typeof(AeroPlane);
                if (_type.IsClass)
                {
                    ConstructorInfo[] _cInfo = _type.GetConstructors();
                    foreach (ConstructorInfo _ci in _cInfo)
                    {
                        Console.WriteLine(_ci.Name);
                    }
                    object obj = Activator.CreateInstance(_type);
                    MethodInfo _minfo = _type.GetMethod("Model", BindingFlags.Instance | BindingFlags.NonPublic);
                    if (_minfo != null)
                    {
                        _minfo.Invoke(obj, null);
                    }
                    MethodInfo[] _mInfoCollection = _type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
                    foreach (MethodInfo _mm in _mInfoCollection)
                    {
                        ParameterInfo[] _pInfo = _mm.GetParameters();
                        if (_pInfo.Length != 0)
                        {
                            object[] _param;
                            if (_mm.Name.Equals("DoSum"))
                            {
                                _param = new object[] { 2, 4 };
                            }
                            else
                            {
                                _param = new object[] { "Ashutosh", 23 };
                            }
                            _mm.Invoke(obj, _param);
                        }
                        else
                        {
                            _mm.Invoke(obj, null);
                        }
                    }
                }
            }
        }
    }
在上面的类中,正如你所看到的,我已经检索了 aeroplane 类的类型。Type 是一个类,它包含类型是什么,例如数组、类、接口、值类型等。然后,在 Proceed 方法中,我使用 typeof() 关键字将我的 AeroPlane 类的类型传递给这个变量,这将把类型带入我的 _type 变量中。现在,我将做的所有事情都将来自这个 _type 变量。在下一行,我正在检查 _type 是否是一个类。如果它是一个类类型,那么我正在通过 constructorinfo 检查这个类中的构造函数。ConstructorInfo 提供了指定类型中构造函数的详细信息。在 foreach 循环中,我正在尝试检索构造函数的名称,这将给我 .ctor 作为输出。
好的,现在是重头戏。在下一行,我创建了一个名为“obj”的对象,在其中我调用了 Activator.CreateInstance(_type)。这行代码实际上将创建我的 _type Type 的一个实例,它实际上指向 AeroPlane 类。所以现在在我的对象中,我有一个 AeroPlane 类的实例。这是我需要在运行时调用我的方法所需要的。为了更好地理解,请参考下面的图片,它将显示在运行时进入 obj 的实例。
 
现在,让我们继续下一行,我正在使用“MethodInfo”。MethodInfo 用于从该 Type 中检索方法,以便我们可以在代码中进一步使用它们。在我的代码中,你可以看到我正在使用 _type.GetMethod(“Model”) 并带有一些 bindingflag 参数来检索 MethodInfo。有两种方法可以获取方法详细信息:一种是使用 _type.GetMethods() 获取所有方法到数组中,第二种是我上面使用的 _type.GetMethod(“MethodName”)。使用 _type.GetMethod(“”),你可以在 MethodInfo 对象中获取一个特定的方法。bindingflag 是一个枚举,它帮助你提取 Type 的信息,bindingflag.instance 会获取实例,bindingflag.NonPublic 会获取私有方法等非公共方法,以便你可以在代码中稍后使用它们。所以下面的代码将获取 Model(它本质上是私有的),并且我将使用 MethodInfo.Invoke 方法来调用它,这样我就可以看到该方法的输出了。正如你所看到的,Invoke 方法将 object obj 作为第一个参数,这就是我们之前在代码中创建这个对象的原因,用于调用方法。为了更好地理解,请参考下面的图片。
 
在上面的图片中,你可以看到,我已经将 Model 方法检索到我的 MethodInfo 对象中,现在我可以用这个对象做很多事情。
现在,让我们继续,当你不想在调用方法时指定具体的方法时,简单来说,当你想要检索所有方法详细信息并逐个调用它们时。正如我之前所说,有两种方法可以检索方法详细信息;我们已经看到了第一种方法,用于获取特定方法的详细信息,现在我们将使用下一方法,它将把所有方法提取到 MethodInfo 数组对象中,即 _type.GetMethods()。为了更好地理解,请看下面的图片。

 
在上面的代码中,你可以看到我已经将所有非公共方法的详细信息检索到了 _mInfoCollection 对象中。现在我们将逐个调用它们;正如你所看到的,大多数方法都是 void 且不接受任何参数;除了 DoSum()(它接受两个 Int32 参数)和 Age()(它接受 String 和 Int32 作为参数)之外。所以我们将使用 MethodInfo.Invoke() 方法调用我们的无参数方法,而对于那些接受参数的方法,我们将使用 ParameterInfo 来调用它们。
在我们开始理解代码之前,请先看下面的图片。

 
如上面的代码所示,我正在遍历 _mInfoCollection,它目前是我的方法集合。现在,我正在尝试使用 ParameterInfo[] 来访问它们的参数,并通过检查它们的 length 属性来判断它们是否接受任何参数。如果它们包含任何参数,我将根据方法名称创建对象数组并传递我的参数,并在调用时将第二个参数设置为 obj,请记住,我们之前将其传递为 NULL。所以它将调用带参数的参数化方法,就像我们传递的那样,而对于那些不带任何参数的方法,你可以将其传递为 NULL。
所以最后,如果你执行了代码,你将找到下面的输出。

 
希望这让你对 .NET 中的反射有了一些了解。
© . All rights reserved.