Chapter 12 - Reflection and Attribute

 The content and code of this article is referenced from book Pro C#5.0 and the .NET 4.5 Framework by Apress. The intention of the writing is to review the konwledge and gain better understanding of the .net framework. 

 

1. Understanding Reflection

In .net, Reflection is the process of runtime type discovery. Through reflection, you can obtain a list of types contained within a given dll or exe, including methods, fields, properties and events defined by a given type. 

System.Reflection namespace. 

Type   Meaning
Assembly  This abstract class contains a number of static methods that allow you to load, investigate, and manipulate an assembly. 
AssemblyName  This class allows you to discover numerous details behind an assembly's identity
EventInfoThis abstract class holds information about a given event
FieldInfo  This abstract class holds information about a given field
MemberInfo   This is the abstract base class that defines common behaviors for the EventInfo, FieldInfo, MethodInfo, and PropertyInfo types
ModuleThis abstarct class allows you to access a given module within a multifile assembly 
ParameterInfoThis class holds information for a given parameter
PropertyInfoThis class holds information for a given property
  

1.1 System.Type class

The System.Type class defines a number of members that can be used to examine a type's metadata. For example, Type.GetMethods() returns array of MethodInfo objects. 

Type Meaning

IsAbstract

IsArray

IsClass

IsCOMObject

IsEnum

IsGenericTypeDefinition

IsGenericParameter

IsInterface

IsPrimitive

IsNestedPrivate

IsNestedPublic

IsSealed

IsValueType

allows you to discover a number of basic traits about the type you are referring to

GetConstructors()

GetEvents()

GetFields()

GetInterfaces()

GetMembers()

GetMethods()

GetNestedTypes()

GetProperties()

These methods allow you to obtain an array representing the items (interface, method, property, etc) you are interested in. 
FindMembers()This method returns a MemberInfo array based on search criteria
GetType()This static method returns a Type instance given a string name
InvokeMember()  This method allows "late binding" for a given item. 

1.2 Obtaining type reference from System.Object.GetType()

You can obtain an instance of the type in a variety of ways. 

        public static void Main (string[] args)
        {
            Car car = new Car ();
            Type t = car.GetType ();
        }

Obviously, this approach will only work if you have compile-time knowledge of the type you wish to reflect over and currently have an instance of the type in memory. 

 

1.3 Obtaining type reference using typeof() operator

        public static void Main (string[] args)
        {
            Type t = typeof(Car);
        }

Unlike object.GetType(), the typeof operator is helpful in that you do not need to first create an object instance. However, your code base must still have compile knowledge of the type .

 

1.4 Obtaining type reference using System.Type.GetType

To obtain type information in a more flexible manner, you may call the static GetType() member of the System.Type class and specify the full qualified string name of the type. 

The Type.GetType() method has been overloaded to allow you to specify two boolean parameters, one is whether an exception should be thrown if the type cannot be found, and the other of which establishes the case sensitivity of the string. 

        public static void Main (string[] args)
        {
            //don't throw exception, and ignore case
            Type t = Type.GetType ("Car", false, true);
        }

When you wish to obtain metadata for a type within an external assemlby, the string is formatted using the type's full qualified name, followed by a comma, followed by the friendly  name of the assembly name. 

        public static void Main (string[] args)
        {
            //don't throw exception, and ignore case
            Type t = Type.GetType ("CarLibrary.Car, CarLibrary", false, true);
        }

As well, you may pass token + to denote a nested type. 

Type t = Type.GetType("CarLibrary.Car+CarOptions");

 

2. Building Custom Metadata Viewer

2.1 Reflecting on Methods

        //Display method names of type. 
        static void ListMethods(Type t)
        {
            MethodInfo[] mi = t.GetMethods ();
            foreach (MethodInfo m in mi) {
                Console.WriteLine (m.Name);
            }
            Console.WriteLine ();
        }

 MethodInfo has many additional members that allow you to determine whether the method is static, virtual, generic, or abstract. 

        static void ListMethods(Type t)
        {
            var mi = from m in t.GetMethods ()
                     select m.Name;
            foreach (var name in mi) {
                Console.WriteLine (name);
            }
        }

 

2.2 Reflection on fields and properties

        static void ListFields(Type t)
        {
            var fieldNames = from f in t.GetFields ()
                             select f.Name;
            foreach (var name in fieldNames) {
                Console.WriteLine (name);
            }
        }
        static void ListProperties(Type t)
        {
            var propNames = from p in t.GetProperties ()
                            select p.Name;
            foreach (var name in propNames) {
                Console.WriteLine (name);
            }
        }

 

2.3 Reflecting on interfaces

        static void ListInterfaces(Type t)
        {
            var ifaces = from i in t.GetInterfaces ()
                         select i;
            foreach (Type i in ifaces) {
                Console.WriteLine (i.Name);
            }
        }

 

2.4 Displaying other info

        static void ListVariousStats(Type t)
        {
            Console.WriteLine ("Base class is {0}", t.BaseType);
            Console.WriteLine ("Is type abstract? {0}", t.IsAbstract);
            Console.WriteLine ("Is type sealed? {0}", t.IsSealed);
            Console.WriteLine ("Is type generic? {0}", t.IsGenericTypeDefinition);
            Console.WriteLine ("Is type abstract? {0}", t.IsClass);
        }

 

2.5 Reflecting on generic type

When you call Type.GetType() in order to obtain metadata description of generic types, you must make use of a special syntax involving (`) followed by a numerical value that represents the number of type parameters the type supports. 

System.Collections.Generic.List<T>, you need to pass "System.Collections.Generic.List `1"

System.Collections.Generic.Dictionary<TKey, TValue>, you need to pass "System.Collections.Generic.Dictionary `2"

 

2.6 Reflecting on Method Parameters and Return values

Now, we update the ListMethods() helper function to list not only the name of a given method, but also the return type and incoming parameter types. The MethodInfo type provides the ReturnType property and GetParameters() method for these very tasks. 

        static void ListMethods(Type t)
        {
            MethodInfo[] mi = t.GetMethods ();

            foreach (var m in mi) {
                //get return type
                string returnType = m.ReturnType.FullName;
                string paramInfo = "( ";
                foreach (var p in m.GetParameters()) {
                    paramInfo += string.Format ("{0} {1}", p.ParameterType, p.Name);
                }
                paramInfo += " )";

                Console.WriteLine ("{0} {1} {2}", returnType, m.Name, paramInfo);
            }
        }

As an shortcut, be aware that each of the XXXInfo types have overridden ToString() to display the signature of the item requested. 

        static void ListMethods(Type t)
        {
            var methodNames = from n in t.GetMethods ()
                              select n;

            foreach (var m in methodNames) {
                Console.WriteLine (m);
            }
        }

 

3. Dynamically loading assembly

We learned about how CLR consults the assembly manifest when probing for an externally referenced assembly. But, sometimes, there will be many times when you need to load assemblies on the fly, and the act of loading external assemblies on demand is known as a dynamic load. 

System.Reflection defines a class named Assembly, and using this class you are able to dynamically load both private assemlby and shared assembly. 

 

        static void DisplayTypesInAssembly(Assembly asm)
        {
            Console.WriteLine (asm.FullName);
            Type[] types = asm.GetTypes ();

            foreach (Type t in types) {
                Console.WriteLine ("Type: {0}", t);
            }
        }


        public static void Main (string[] args)
        {
            Assembly asm = null;
            try{
                asm = Assembly.Load("name");
                DisplayTypesInAssembly(asm);
            }
            catch {
                Console.WriteLine ("Error occurred");
            }
        }

If you wish to reflect over car.dll, you will need to copy the car.dll binary to \bin\Debug directory of the applicaiton to run this program. 

You can use Assembly.LoadFrom() rather than Assembly.Load, to pass in the absolute path to the dll file. 

Assembly.LoadFrom(@"C\myApp\car.dll");

 

3.1 shared assemblies

To load a shared assembly from GAC, the Assembly.Load() parameter must specify a PublicKeyToken value. 

        public static void Main (string[] args)
        {
            string displayName = null;
            displayName = "System.Windows.Forms," + "Version=4.0.0.0," +
            "PublicKeyToken=b77a5c561934e089";
            Assembly asm = Assembly.Load (displayName);
            DisplayTypesInAssembly (asm);
        }

 

4. Understanding late binding

Late binding is a technique in which you are able to create an instance of a given type and invoke its members at runtime without having hard-coded compile-time knowledge of its existence. 

 

4.1 System.Activator class

The System.Activator class is the key to the .net late-binding process. Activator.CreateInstance() method, is used to create an instance of a type for late binding. The simplest variation of the CreateInstance() member takes a valid Type object that describes the entity you wish to allocate into memory on the fly. 

    static void CreateLateBinding(Assembly asm)
        {
            try{
                //get type
                Type t = asm.GetType("Car");

                //invoke the type
                object obj = Activator.CreateInstance(t);
            }
            catch(Exception ex) {
                Console.WriteLine (ex.Message);
            }
        }

Notice that the Activator.CreateInstance() method returns a System.Object rather than a strongly typed data. And you can't cast the object to specific type , because we have not set a reference to dll file. 

 

4.2 Invoking methods 

        static void CreateLateBinding(Assembly asm)
        {
            try{
                //get type
                Type t = asm.GetType("Car");
                //create instance on the fly
                object obj = Activator.CreateInstance(t);
                //get method info
                MethodInfo m = t.GetMethod("Print");
                //invoke method, null for 0 parameters
                m.Invoke(obj, null);
            }
            catch(Exception ex) {
                Console.WriteLine (ex.Message);
            }
        }

 

4.3 Invoking methods with parameters

When you wish to use late binding to invoke a method requiring parameters, you should package up the arguments as a loosely typed array of objects. 

 m.Invoke(obj, new object[]{true, 2});
 
5. Attribute

The .net platform provides a way for programmers to embed additional metadata into an assembly using attributes. In a netshell, attributes are nothing more than code annotations that can be applied to a given type, member, assembly. 

.NET attributes are class types that extend the abstract System.Attribute base class. .net provides us with many predefined attributes, and you are free to build you own custom attributes. 

[CLSCompliant]Enforces the annotated item to conform to the rules of the CLS
[DllImport]Allows .net code to make calls to any unmanged c or c++ based code library
[Obsolete]Marks a deprecated type or member. 
[Serializable]Marks a class or structure as being "serializable", it is able to persist its current state into a stream
[NonSerialized]Specifies that a given field in a class or structure should not be persisted during the serialization process
[ServiceContract]Marks a method as a contract implemented by a WCF servcie
  
  

 

5.1 Applying attribute

    [Serializable]
    public class MotorCycle
    {
        public MotorCycle ()
        {

        }
    }
    [Serializable, Obsolete("user other class")]
    public class MotorCycle
    {
        public MotorCycle ()
        {

        }
    }

 

5.2 Shorthand notation

The actual class name of the [Obsolete] attribute is ObsoleteAttribute, not Obsolete. However, to simply the process of applying attributes, the C# language does not require you to type in Attribute suffix. 

Attributes are classes that derive from System.Attribute

Attributes result in embedded metadata

Attributes are basically useless until another agent reflects upon them

 

5.3 Define custom attribute

    public sealed class VehicleDescriptionAttribute : System.Attribute
    {
        public string Description {
            get;
            set;
        }

        public VehicleDescriptionAttribute (string desc)
        {
            Description = desc;
        }

        public VehicleDescriptionAttribute ()
        {
            
        }
    }
    [Serializable]
    [VehicleDescription (Description="My car")]
    public class MotorCycle
    {
        public MotorCycle ()
        {

        }
    }

 

5.4 Restricting attribute usage

Sometimes, you may want to build a custom attribute that can be applied only to select code elements, and then you need [AttributeUsage] on the definition of your custom attributes. 

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited=false)]
    public sealed class VehicleDescriptionAttribute : System.Attribute

 

5.5 Assembly-level attributes

It is possible to apply attributes on all types within a given assembly using the [assembly:] tag. Be very aware that all assembly or module level attributes must be listed outside the scope of any namespace scope. 

[assembly: CLSCompliant(true)]
namespace NetPractise

 

5.6 AssemblyInfo.cs

The file is a handy place to put attributes that are to be applied at the assembly level. 

 

5.7 Reflecting on attributes using early binding

If you wish to make use of early binding, you'll require the client application to have compile-time definition of the attribute in question. 

        static void RelfectAttributeEarylyBinding()
        {
            Type t = typeof(MotorCycle);

            object[] customAtts = t.GetCustomAttributes (false);

            foreach (VehicleDescriptionAttribute a in customAtts) {
                Console.WriteLine (a.Description);
            }
        }

 

5.8 Reflecting on attributes using late binding

        static void ReflectAttributesUsingLateBinding()
        {
            try{
                Assembly asm = Assembly.Load("CarLibrary");
                //get type
                Type vechicleDesc = asm.GetType("CarLibrary.VehicleDescriptionAttribute");
                //get property
                PropertyInfo propDesc = vechicleDesc.GetProperty("Description");

                Type[] types = asm.GetTypes();

                foreach(Type t in types)
                {
                    object[] objs = t.GetCustomAttributes(vechicleDesc, false);

                    foreach(object o in objs)
                    {
                        Console.WriteLine(propDesc.GetValue(o, null));
                    }
                }
            }
            catch(Exception ex) {
                Console.WriteLine (ex.Message);
            }
        }

 

转载于:https://www.cnblogs.com/timBo/p/4422800.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值