(Reflection)认识反射(反射实例化,反射评价,在工厂三层架构应用)

来源:互联网 发布:怎样找淘宝客 编辑:程序博客网 时间:2024/05/01 17:21

反射的基础概念:

反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。

  NET可执行应用程序结构

  程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构。

  应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。

  程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。反射通常具有以下用途。

  (1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。

  (2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其它特定的非全局方法。

  (3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic private)和实现详细信息(如abstractvirtual)等。使用TypeGetConstructorsGetConstructor方法来调用特定的构造函数。

  (4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic private)和实现详细信息(如abstractvirtual)等。使用TypeGetMethodsGetMethod方法来调用特定的方法。

  (5)使用FiedInfo了解字段的名称、访问修饰符(如publicprivate)和实现详细信息(如static)等,并获取或设置字段值。

  (6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。

  (7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。

  (8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。

  System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。

  反射也可用于创建称为类型浏览器的应用程序,使用户能够选择类型,然后查看有关选定类型的信息。

  此外,Jscript等语言编译器使用反射来构造符号表。System.Runtime.Serialization命名空间中的类使用反射来访问数据并确定要永久保存的字段,System.Runtime.Remoting命名空间中的类通过序列化来间接地使用反射。

 

( 1 ) 反射什么时候可以使用:

使用反射工厂的优点是极大地减少了工厂类的数量、降低了代码的冗余,并且系统更容易扩展,在增加新类型后,不需要修改工厂类。理论上可以用一个工厂完成很多类型的实例化。多类型的实例化可以通过反射轻松实现。

但是,相应地反射得消耗是非常大的,使用时应该首先考虑清楚这一点。

 

( 2 )反射入门(一些简单实例)

实例

在程序去得时动态实例化对象,获得对象的属性,并调用对象的方法。

1Namespace ReflectionExample

2{

3 class Class1

4 {

5 [STAThread]

6 static void Main (string [ ] args)

7 {

8  System.Console.WriteLine(“列出程序集中的所有类型”);

9  Assembly a = Assembly.LoadFrom (ReflectionExample.exe);

10  Type[ ] mytypes = a.GetTypes( );

11

12  Foreach (Type t in mytypes) //循环所有类型Type

13  {

14   System.Console.WriteLine ( t.Name );

15  }

16  System.Console.ReadLine ( );

17  System.Console.WriteLine (“列出HellWord中的所有方法” );

18  Type ht = typeof(HelloWorld);

19  MethodInfo[] mif = ht.GetMethods();

20  foreach(MethodInfo mf in mif) //循环所有方法MethodInfo[]

21  {

22   System.Console.WriteLine(mf.Name);

23  }

24  System.Console.ReadLine();

25  System.Console.WriteLine("实例化HelloWorld,并调用SayHello方法");

26  Object obj = Activator.CreateInstance(ht);

27  string[] s = {"zhenlei"};

28  Object bojName = Activator.CreateInstance(ht,s); // 实例化CreateInstance

29  BindingFlags flags = (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly);

30  MethodInfo msayhello = ht.GetMethod("SayHello");

31  msayhello.Invoke(obj,null); //调用方法Invoke

32  msayhello.Invoke(objName,null);

33  System.Console.ReadLine();

34  }

35 }

36}

1using System; //被使用的Dll

2namespace ReflectionExample

3{

4  public class HelloWorld

5  {

6   string myName = null;

7   public HelloWorld(string name)

8   {

9    myName = name;

10   }

11   public HelloWorld() : this(null)

12   {}

13   public string Name

14   {

15    get

16    {

17     return myName;

18    }

19   }

20   public void SayHello()

21   {

22    if(myName == null)

23    {

24     System.Console.WriteLine("Hello World");

25    }

26    else

27    {

28     System.Console.WriteLine("Hello," + myName);

29    }

30   }

31  }

32}

33 

实例2

用反射调用任意.net库中的方法(函数中含详尽解述)

 

函数如下:ReturnMessage自定议类

2、通过NameSpaceAndClassNameMethodName实际上就可以精确定位一个方法了如调用abc.dll里的namespace1.Class1.Main,调用起来就是CallAssembly("abc.dll","namespace1.Class1","Main",null)

 

public static ReturnMessage CallAssembly(string Path,string NameSpaceAndClassName,string MethodName,object[] Parameters)

{

       try

       {

          Assembly Ass=Assembly.LoadFrom(Path);//调入文件(不限于dll,exe亦可,只要是.net)

          Type TP=Ass.GetType(NameSpaceAndClassName);//NameSpaceAndClassName"名字空间.类名","namespace1.Class1"

          MethodInfo MI=TP.GetMethod(MethodName);//MethodName是要调用的方法名,"Main"

          object MeObj=System.Activator.CreateInstance(TP);

          MI.Invoke(MeObj,Parameters);//Parameters是调用目标方法时传入的参数列表

          return new ReturnMessage(true,"成功调用",1);

        }

       catch(Exception e)

        {

          return new ReturnMessage(false,"出现异常,消息为:"+e.Message,-1,e);

        }

    }

 

实例3

(1)namespace ClassLibrarySport

{

    public abstract class Sport

    {

        protected string name;

        public abstract string GetName();

        public abstract string GetDuration();

    }

}

= = = = = == = == = == = == = == = == = == = == = == = == = == = == = == = == =

(2)

namespace ClassLibrarySomeSports//该项目添加了对(1)的引用

{

    public class Football : ClassLibrarySport.Sport

    {

        public Football()

        {

            name = "Football";

        }

        public override string GetName()

        {

            return name;

        }

        public override string GetDuration()

        {

            return "four 15 minute quarters";

        }

    }

}

= = = = = == = == = == = == = == = == = == = == = == = == = == = == = == = == =

(3)namespace ConsoleAssemblyTest//该项目添加了对(1)的引用

{

    class Program

    {

        static void Main(string[] args)

        {

            Assembly assembly = Assembly.LoadFrom(@"E:/ClassLibrarySomeSports/bin/Debug/ClassLibrarySomeSports.dll");

            Type[] types = assembly.GetTypes();

             Console.WriteLine("Get Type From ClassLibrarySomeSports.dll:");

            for (int i = 0; i < types.Length; i++)

            {

                Console.WriteLine(types[i].Name);//通过反射获取dll类型

            }

 //使用GetConstructor()方法获取对应类型的构造器,从而构造出该类型的对象

            Console.WriteLine("Use Method GetConstructor():");

            ConstructorInfo ci = types[0].GetConstructor(new Type[0]);

            ClassLibrarySport.Sport sport = (ClassLibrarySport.Sport)ci.Invoke(new object[0]);

            Console.WriteLine(sport.GetName() + " has " + sport.GetDuration());

            //使用Activator.CreateInstance()方法构造出该类型的对象

//使用assembly.CreateInstance()返回为null

            Console.WriteLine("Use Method CreateInstance():");

            ClassLibrarySport.Sport sport1 = (ClassLibrarySport.Sport)

Activator.CreateInstance(types[0]);

            Console.WriteLine(sport1.GetName() + " has " + sport1.GetDuration());

//反射指定类型中的名称为“GetDuration”的方法,通过Invoke()方法执行该方法

            object objSport = Activator.CreateInstance(types[0]);

            MethodInfo method = types[0].GetMethod("GetDuration");

            object o = method.Invoke(objSport, new object[0]);

            Console.WriteLine(o as string);

            Console.Read();

        }

    }

}

= = = = = == = == = == = == = == = == = == = == = == = == = == = ==

Output://输出结果

Get Type From ClassLibrarySomeSports.dll:

Football

Use Method GetConstructor():

Football has four 15 minute quarters

Use Method CreateInstance():

Football has four 15 minute quarters

four 15 minute quarters

 

 

( 3 ) 简单谈谈反射工厂

        借网上一个实例,在此再简单论述一下反射在工厂三层中的应用,以作深一步理解使用反射。
        例:假设我们需要创建一种交通工具,可以是汽车、火车或者轮船。

         采用简单工厂方法来实现,代码如下:
采用接口定义了抽象的工厂方法

public Interface CreateVehicleInterface CreateVehicle 定义一个产生交通工具的接口
     Function CreateAVehicle()Function CreateAVehicle() As Vehicle `创建一个交通工具
End Interface
具体的创建由子类决定
public Class CreateCarClass CreateCar
    Implements CreateCar
    Public Function CreateAVheicle()Function CreateAVheicle() AsVehicle Implements
CreateVehicle.CreateAVehicle
        Return New Car
     End Function
End Class

     如果我们希望增加一个新的交通工具,需要实现工具接口和产生交通工具的工厂方法。

1.一般工厂做法:

Public Class CreateBoatClass CreateBoat
       Implements CreateVehicle
       Public Function CreateAVehicle()Function CreateAVehicle() As Vehicle Implements
CreateVehicle.CreateAVehicle
            Return New Boat
       End Function
    End Class

2.采用反射技术简化工厂类

1.首先查看采用反射技术实现简化的实例:

Imports System.Reflection
Public Class CreateVehicleByTypeClass CreateVehicleByType
Implements CreateVehicle

Private VeicleType As Type

Public Sub New()Sub New(ByVal t As Type)
VeicleType = t
End Sub

Public Function CreateAVehicle()Function CreateAVehicle() As Vehicle Implements
CreateVehicle.CreateAVehicle
           Dim objConstructor As ConstructorInfo =
VeicleType.GetConstructor(System.Type.EmptyTypes)
           Dim c As Vehicle = Ctype(objConstructou.Invoke(Nothing),Vehicle)
           Return c 
       End Function
End Class

2. 在使用时,只要在创建时带入需要创建的类的类型:

Private Sub btcreateByType_Click()Sub btcreateByType_Click(ByVal sender As System.Object,ByVal e As
System.EventArgs) Handles btCreateBytype.Clik
    `根据选择创建一个交通工具并执行GO
    Dim v As Vehicle `我们不知道需要创建的具体交通工具
    Dim f As CreateVehicle
    If rCar.Checked Then
        F = New CreateVehicleByType(GetType(car))
    End If
    If rTrain.Checked Then
        F = New CreateVehicleByType(GetType(Train))
    End If
    If rBoat.Checked Then
        F = New CreateVehicleByType(GetType(Boat))
    End If
    If rBus.Checked Then
         F = New CreateVehicleByType(GetType(Boat))
     End If
     V = f.CreateAVehicle
     `执行GO指令
     lbGO.Text = v.Go
  End Sub

 

3.对简单工厂的改进

简单工厂通过参数决定创建的类型,这些参数是在编程时预设的。因此在编译后就无法修改,让我们回顾代码:

我们可以将这个工厂改造为反射工厂://这里不是太明_下面代码如何体现反射工厂的作用

Public Class clsCreateDBClass clsCreateDB
        Public Shared Function CreateDB()Function CreateDB(ByVal strType As stringByVal strConnString AsString) As _ clsAbstractDB
            Select Case strType.ToUpper
                Case “ORACLE”
                    Dim myOracle As clsoracleDB
                    MyOracle = New clsOracleDB(strConnString)
                    Return myOracle
                Case “SQLSERVER”
                    Dim mysqlserver As clsSQLServerDB
                    Mysqlserver = New clsSQLServerDB(strConnString)
                    Return mysqlserver
                Case Else
                    Dim myoledb As clsOLEDB
                    Myoledb = New clsOLEDB(strConnString)
                    Return myoledb
             End Select
         End Function
     End Class

4.反射与工厂方法
        如果工厂方法仅仅是为了获得某个产品的实例,那么完全可以使用反射技术来实现工厂方法。这样解决了工厂方法的潜在问题,即当增加产品类时,必须增加相应的子类。
         然而当工厂方法所存在的类不仅是实例化产品时,采用反射不一定是好办法,因为可能使问题变得复杂。

5.反射与抽象工厂
         可以采用反射来实现抽象工厂,这时抽象工厂可能变成了使用反射技术的具体工厂,不再有子类存在。创建交通系统的实例可以用如下的代码来写:

'<summary>
'VehicleSystemReflectionFactory 采用反射技术的工厂。
'</summary>
Public Class VehicleSystemReflectionFactoryClass VehicleSystemReflectionFactory
    Dim vehicleType As String
    Dim vehicleStationType As String
    Public Sub New()Sub New(ByVal vt As StringByVal vst As String)
        Me.vehicleType = vt
        Me.vehicleStationType = vst
    End Sub
    Public Function GetVehicle()Function GetVehicle() As Vehicle
        Return CType(createbytype(Me.vehicleType), Vehicle)
    End Function

    Public Function GetVehicleStation()Function GetVehicleStation() As VehicleStation
        Return CType(createbytype(Me.vehicleStationType), VehicleStation)
    End Function
    Private Function createbytype()Function createbytype(ByVal vt As StringAs Object
        Dim tt As Type
        tt = Type.GetType(vt)
        Dim ci As ConstructorInfo
        ci = tt.GetConstructor(System.Type.EmptyTypes)
        Dim null As System.DBNull
        Return ci.Invoke(null)
    End Function

End Class

6.反射工厂的使用效果
         使用反射工厂的优点是极大地减少了工厂类的数量、降低了代码的冗余,并且系统更容易扩展,在增加新类型后,不需要修改工厂类。
         使用反射工厂的代价是工厂与产品之间的依赖关系不明显,由于是动态绑定,因此理论上可以用一个工厂完成很多类型的实例化,从而使得代码 不容易理解。另外增大了测试难度,创建是动态完成的,测试用例的编写和测试执行要比传统的工厂困难。