反射

一、获取Type对象

 1 using System;
 2 
 3 namespace TypeDemo
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             DateTime dateTime = new DateTime();
10 
11             // 获取一个 Type 对象方法一:
12             // 成功调用 GetType 的关键在于获得的是一个对象实例的 Type 类型。
13             // 注:他不能对静态类使用 GetType。
14             Type type = dateTime.GetType();
15             foreach (var propertyInfo in type.GetProperties())
16             {
17                 Console.WriteLine(propertyInfo.Name);
18             }
19 
20             // 获取一个 Type 对象方法二:
21             // typeof 在编译时绑定到一个特定的 Type 对象上,并直接获取这个类型作为参数。
22             MyEnum myEnum;
23             myEnum = (MyEnum) Enum.Parse(typeof (MyEnum), "123");
24             Console.WriteLine(myEnum);
25         }
26 
27         enum MyEnum
28         {
29             
30         }
31     }
32 }

二、预定义特性:

 1 #define COMDITION_A
 2 
 3 using System;
 4 using System.Diagnostics;
 5 
 6 namespace PredefineFeatureDemo
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             Console.WriteLine("Begin...");
13             MethodA();
14             MethodB();
15             Console.WriteLine("End...");
16         }
17 
18         // 类似于用一堆#if/#endif把方法调用包围起来。
19         // 区别:
20         // 1.ConditionalAttribute不影响目标方法本身的已编译的CLI代码(除了添加必要的特性元数据之外)
// 也就说被他修饰的方法仍然会被编译。
21 // 2.ConditionalAttribute会在编译期间通过移除调用的方式来影响调用点。 22 // 作用: 23 // 由于是否调用一个方法,不是取决与被调用者所在的程序集中的预处理器标识符
// 而是取决于调用者所在的程序集中的预处理器标识符,
24 // 故在许多需要进行跟踪和测试的情形下,这个特定就非常有用了。 25 // 限制条件: 26 // 1.假如一个方法包含了out参数,或者返回类型不为void,就不能使用这个特性。 27 // 2.只能用来修饰方法和类,不能用来修饰属性,且修饰的类必须派生自System.Attribute。 28 // 3.要用反射获取那个自定义特性,就必须在调用程序集中定义条件字符串。 29 [Conditional("COMDITION_B")] 30 private static void MethodB() 31 { 32 Console.WriteLine("MethodB excuting..."); 33 } 34 35 [Conditional("COMDITION_A")] 36 private static void MethodA() 37 { 38 Console.WriteLine("MethodA excuting..."); 39 } 40 } 41 }

要想让这种特性在运行时,被执行,有两种方法:

  1. 在改程序集中添加预处理器标识符,如上述例子,在程序集的开头,添加了#define COMDITION_A标识,这样被COMDITION_A标识下的函数就可以被执行了
  2. 在该程序集的属性-生成页签中,在条件编译符号下进行说明,如COMDITION_B,这样被COMDITION_B标识下的函数就可以被执行了

三、反射

由于一些原因这个例子被修改过多次,导致整个逻辑可能有点混乱,但是对于初学者来讲,同样能把发射相关的概念,介绍清楚:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace DynamicCalled
{
    class CommandLineHandler
    {
        public static void Parse(string[] args, object commandLine)
        {
            string errorMessage;
            if (!TryParse(args, commandLine, out errorMessage))
            {
                throw new ApplicationException(errorMessage);
            }
        }
        public static bool TryParse(string[] args, object commandLine, out string errorMessage)
        {
            bool success = false;
            errorMessage = null;

            Dictionary<string, PropertyInfo> options = CommandLineSwitchAliasAttribute.GetSwitches(commandLine);

            foreach (var arg in args)
            {
                PropertyInfo property;
                string option;

                if (arg[0] == '/' || arg[0] == '-')
                {
                    string[] optionParts = arg.Split(new char[] {':'}, 2);

                    option = optionParts[0].Remove(0, 1).ToLower();// 移除斜杠。

                    if (options.TryGetValue(option, out property))
                    {
                        success = SetOption(commandLine, property, optionParts, ref errorMessage);
                    }
                    else
                    {
                        success = false;
                        errorMessage = string.Format("Option '{0}'is not supported.", option);
                    }
                }
            }
            return success;
        }

        private static bool SetOption(object commandLine, PropertyInfo property, string[] optionParts, ref string errorMessage)
        {
            bool success;
            if (property.PropertyType == typeof(bool))
            {
                // commandLine 要设置值的对象,设置的新值,以及一个额外的索引(除非属性是一个索引引起,否则,该参数为NULL。)
                // 使用来自命令行的数据初始化。
                property.SetValue(commandLine, true, null);
                success = true;
            }
            else
            {
                if ((optionParts.Length < 2) || optionParts[1] == "" || optionParts[1] == ":")
                {
                    success = false;
                    errorMessage = string.Format("You must specify the value for the {0} option.", property.Name);
                }
                else if (property.PropertyType == typeof(string))
                {
                    // 通过 SetValue 方法向 CommandLineInfo 类的实例属性中赋值。
                    // 通过 GetValue 方法可以从 CommandLineInfo 类中取出相关属性值。
                    property.SetValue(commandLine, optionParts[1], null);
                    success = true;
                }
                else if (property.PropertyType.IsEnum)
                {
                    try
                    {
                        property.SetValue(commandLine,Enum.Parse(typeof(ProcessPriorityClass),optionParts[1],true),null);
                        success = true;
                    }
                    catch(ArgumentException)
                    {
                        success = false;
                        errorMessage = string.Format("The option '{0}' is " + "invalid ofr'{1}'", optionParts[1],
                                                     optionParts);
                    }
                }
                else
                {
                    success = false;
                    errorMessage = string.Format("Data type '{0}' on {1} is not supported.",
                                                 property.PropertyType.ToString(), commandLine.GetType().ToString());
                }
            }
            return success;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Reflection;

namespace DynamicCalled
{
    /// <summary>
    /// 可以类似这样定义部分类,但是他们的文件名不允许相同。
    /// 注:虽然 CommandLineInfo 是一个私有类,但是在这上面执行反射一点问题都没有,甚至可以调用他的其他成员。
    ///     换言之,反射可以绕过可访问性规则。
    /// </summary>
    public partial class Program
    {
        private class CommandLineInfo
        {
            [CommandLineSwitchAlias("?")]
            public bool Help
            {
                get { return _Help; }
                set { _Help = value; }
            }
            private bool _Help;

            /// <summary>
            /// 即:假如 Out 是私有的,CommandLineHandler 类 TryParse 方法中,仍然可以向 Out 赋值。
            /// </summary>
            [CommandLineSwitchAlias("FileName")
            ,CommandLineSwitchRequired]
            public string Out
            {
                get { return _Out; }
                set { _Out = value; }
            }
            private string _Out;

            public ProcessPriorityClass Priority
            {
                get { return _Priority; }
                set { _Priority = value; }
            }
            private ProcessPriorityClass _Priority = ProcessPriorityClass.Normal;
        }
    }

    internal class CommandLineSwitchRequiredAttribute : Attribute
    {
        // 为了实现更好的封装性,可以把查找某个特性的代码放到自定义特性之内,并设置为静态的方法。
        public static string[] GetMissingRequiredOptions(object commandLine)
        {
            StringCollection missingOptions = new StringCollection();

            // 获取 commandLine 对象的所有属性集合。
            PropertyInfo[] properties = commandLine.GetType().GetProperties();

            foreach (var property in properties)
            {
                // 获取当前单独的一个属性的自定义符合 CommandLineSwitchRequiredAttribute 类型的特性。
                Attribute[] attributes = (Attribute[])property.GetCustomAttributes(typeof(CommandLineSwitchRequiredAttribute), false);
                if ((attributes.Length > 0) && (property.GetValue(commandLine, null) == null))
                {
                    missingOptions.Add(property.Name);
                }
            }
            return new string[2];
        }
    }

    // 限制特性修饰的构造,避免不恰当地使用特性。
    // AttributeUsageAttribute是一个预定义特性,没有运行时代码,而是由编译器内创建了对他的支持。
    [AttributeUsage(AttributeTargets.Property)]
    internal class CommandLineSwitchAliasAttribute : Attribute
    {
        /// <summary>
        /// 使用别名时,需要一个构造器来获取一个string参数。
        /// </summary>
        /// <param name="alias"></param>
        public CommandLineSwitchAliasAttribute(string alias)
        {
            Alias = alias;
        }

        public string Alias
        {
            get { return _Alias; }
            set { _Alias = value; }
        }
        private string _Alias;

        // 获取自定义特性。并把每个名称同命令行对象的对应特性关联起来。
        public static Dictionary<string,PropertyInfo> GetSwitches(object commandLine)
        {
            Dictionary<string, PropertyInfo> options = new Dictionary<string, PropertyInfo>();

            // 获取 commandLine 对象的所有属性集合。
            PropertyInfo[] properties =
                commandLine.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (var property in properties)
            {
                options.Add(property.Name.ToLower(),property);
                // 遍历单个属性的自定义特性名为 CommandLineSwitchAliasAttribute 的所有特性的集合。
                // false表示是否要查找自定义属性修饰的的相关信息的继承类或者重写方法。如果是自定义属性中的继承字段true或者false时,表示继承或重写他修饰的相关信息的所有子类或者方法都有这个特性或者方法。
                foreach (CommandLineSwitchAliasAttribute attribute in property.GetCustomAttributes(typeof(CommandLineSwitchAliasAttribute), false))
                {
                    options.Add(attribute.Alias.ToLower(), property);
                }
            }
            // 返回的结果把属性和属性名称以及所有的别名都关联到了一起。
            return options;
        }
    }
}
using System;
using System.Reflection;

namespace DynamicCalled
{
    public partial class Program
    {
        static void Main(string[] args)
        {
            // 搜索具有指定名称的公共属性。
            PropertyInfo property = typeof(CommandLineInfo).GetProperty("Out");
            CommandLineSwitchAliasAttribute attribute =
                (CommandLineSwitchAliasAttribute)
                property.GetCustomAttributes(typeof(CommandLineSwitchAliasAttribute), false)[0];
            if (attribute.Alias == "FileName")
            {
                Console.WriteLine("Out(FileName)");
            }
        }
    }
}

 

posted @ 2013-01-24 15:40  天之涯,海之角  阅读(177)  评论(0编辑  收藏  举报