zoukankan      html  css  js  c++  java
  • 共享一个从字符串转 Lambda 表达式的类(3)

    承上篇的思路继续写,这次介绍字符串转 Type 的方式——类型分析。我的思路是把 Type 解析由“TestNet.Person, TestNet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null” 这种复杂的方式改为 “Person” 这种。这样,写起来简单明了,看起来也简单明了。

    这次要说的东西比较简单,我借用一个例子说明:

    首先,字符串“TestNet.Person”经过解析被分析为下面的 Token 集合:

    1. 标识、索引值 = 0、文本表示 = TestNet
    2. 句点、索引值 = 7、文本表示 = .
    3. 标识、索引值 = 8、文本表示 = Person

    接着,判断标识符是否等于 int、bool 等 C# 基本类型文本名称,是则返回相应的类型( Type 实例);否则借用 Type.GetType 方法解析,进一步借用 Assembly.GetType 方法解析:

    1. 判断 TestNet 不是 int、bool 等 C# 基本类型的文本名称。
    2. 借用 Type.GetType 方法解析,返回值为 null 。
    3. 进一步借用 Assembly.GetType 方法解析,返回值为 null 。
    4. 读取下一个字符 ID = 句点,表示 TestNet 可能是命名空间名称,需要继续读取并分析。
    5. 判断 TestNet.Person 不是 int、bool 等 C# 基本类型的文本名称。
    6. 借用 Type.GetType 方法解析,返回值为 null 。
    7. 进一步借用 Assembly.GetType 方法解析,返回值为 TestNet.Person 类型的 Type 实例。
    8. 分析过程结束。

    实际的过程中,我们还需要为分析方法添加额外的命名空间。这样,像“TestNet.Person” 这样的类型,就可以简写为 “Person”,符合我们使用 C# 的编码规范。

    进一步考虑,我们可以从项目未引用的程序集中提取类型,这时候需要添加额外的 Assembly 。这样做的好处是:可以在不编译整个项目的情况下,更改某个配置,就可以使用新的程序集,新的逻辑。呵呵,是不是有点插件式编程的影子了。下一篇,应该会揭开面纱了。

    下面,我给出类型解析类(TypeParser)的源码:

    /// <summary>
    /// 类型分析器
    /// </summary>
    [DebuggerStepThrough]
    public class TypeParser
    {
        #region Properties
        /// <summary>
        /// 原始字符串分析结果
        /// </summary>
        private SymbolParseResult spResult = null;
        /// <summary>
        /// 获得待分析的类型可能用到的命名空间列表
        /// </summary>
        private IEnumerable<string> namespaces = Enumerable.Empty<string>();
        /// <summary>
        /// 获得额外的程序集信息列表
        /// </summary>
        private IEnumerable<Assembly> assemblyExtensions = Enumerable.Empty<Assembly>();
     
        private TypeParser()
        {
        }
     
        /// <summary>
        /// Initializes a new instance of the <see cref="TypeParser"/> class.
        /// </summary>
        /// <param name="spResult">The symbol parse result.</param>
        internal TypeParser(ref SymbolParseResult spResult)
        {
            this.spResult = spResult;
        }
     
        /// <summary>
        /// 获得一个 <see cref="TypeParser"/> 类的实例对象
        /// </summary>
        public static TypeParser NewInstance
        {
            get { return new TypeParser(); }
        }
     
        private static Assembly[] _assemblies = null;
        /// <summary>
        /// 获取程序入口点所在目录程序集列表
        /// </summary>
        private static Assembly[] Assemblies
        {
            get
            {
                if (_assemblies == null)
                {
                    var directory = string.Empty;
     
                    var assembly = Assembly.GetEntryAssembly();
                    if (assembly != null)
                        directory = Path.GetDirectoryName(assembly.Location);
                    else
                        directory = AppDomain.CurrentDomain.BaseDirectory;
     
                    var files = Directory.GetFiles(directory, "*.dll", SearchOption.TopDirectoryOnly);
     
                    var data = new List<Assembly>(files.Length);
                    foreach (var item in files)
                    {
                        try
                        {
                            data.Add(Assembly.LoadFile(item));
                        }
                        catch { }
                    }
                    _assemblies = data.ToArray();
                }
                return _assemblies;
            }
        }
        #endregion
     
        #region Business Methods
        /// <summary>
        /// 添加可能遇到的命名空间字符串列表
        /// </summary>
        /// <param name="namespaces">新的命名空间字符串列表</param>
        /// <returns>修改后的自身</returns>
        public TypeParser SetNamespaces(IEnumerable<string> namespaces)
        {
            this.namespaces = namespaces ?? Enumerable.Empty<string>();
     
            return this;
        }
     
        /// <summary>
        /// 添加可能遇到的命名空间字符串列表
        /// </summary>
        /// <param name="namespaces">新的命名空间字符串列表</param>
        /// <returns>修改后的自身</returns>
        public TypeParser SetNamespaces(params string[] namespaces)
        {
            this.namespaces = namespaces ?? Enumerable.Empty<string>();
     
            return this;
        }
     
        /// <summary>
        /// 添加可能遇到的程序集信息列表
        /// </summary>
        /// <param name="assemblies">附加的程序集信息列表</param>
        /// <returns>修改后的自身</returns>
        public TypeParser SetAssemblies(IEnumerable<Assembly> assemblies)
        {
            assemblyExtensions = assemblies ?? Enumerable.Empty<Assembly>();
     
            return this;
        }
     
        /// <summary>
        /// 添加可能遇到的程序集信息列表
        /// </summary>
        /// <param name="assemblies">附加的程序集信息列表</param>
        /// <returns>修改后的自身</returns>
        public TypeParser SetAssemblies(params Assembly[] assemblies)
        {
            assemblyExtensions = assemblies ?? Enumerable.Empty<Assembly>();
     
            return this;
        }
     
        /// <summary>
        /// 解析字符串为类型
        /// </summary>
        /// <returns>读取的类型</returns>
        public Type Resolve(string typeString)
        {
            spResult = SymbolParser.Build(typeString);
     
            return ReadType();
        }
        #endregion
     
        #region Private Methods
        internal Type ReadType(string typeName = null, bool ignoreException = false)
        {
            Type type = null;
            StringBuilder sbValue =
                new StringBuilder(string.IsNullOrEmpty(typeName) ? spResult.Next() : typeName);
            do
            {
                // read generic parameters
                if (spResult.PeekNext() == "<")
                {
                    spResult.Skip();
                    List<Type> listGenericType = new List<Type>();
                    while (true)
                    {
                        listGenericType.Add(ReadType());
                        if (spResult.PeekNext() == ",")
                            spResult.Skip();
                        else
                            break;
                    }
                    NextIsEqual(">");
     
                    sbValue.AppendFormat("`{0}[{1}]", listGenericType.Count,
                        string.Join(",", listGenericType
                            .Select(p => "[" + p.AssemblyQualifiedName + "]").ToArray()));
                }
     
                type = GetType(sbValue.ToString());
                if (type == null)
                {
                    bool result = NextIsEqual(".", false);
                    if (!result)
                    {
                        if (ignoreException)
                            break;
                        throw new ParseUnfindTypeException(sbValue.ToString(), spResult.Index);
                    }
                    sbValue.Append(".");
                    sbValue.Append(spResult.Next());
                }
            } while (type == null);
     
            return type;
        }
     
        internal Type GetType(string typeName)
        {
            if (string.IsNullOrEmpty(typeName))
                return null;
     
            // Nullable
            bool isNullable = false;
            if (typeName.EndsWith("?"))
            {
                isNullable = true;
                typeName = typeName.Substring(0, typeName.Length - 1);
            }
     
            Type type;
            switch (typeName)
            {
                case "bool":
                    type = typeof(bool);
                    break;
                case "byte":
                    type = typeof(byte);
                    break;
                case "sbyte":
                    type = typeof(sbyte);
                    break;
                case "char":
                    type = typeof(char);
                    break;
                case "decimal":
                    type = typeof(decimal);
                    break;
                case "double":
                    type = typeof(double);
                    break;
                case "float":
                    type = typeof(float);
                    break;
                case "int":
                    type = typeof(int);
                    break;
                case "uint":
                    type = typeof(uint);
                    break;
                case "long":
                    type = typeof(long);
                    break;
                case "ulong":
                    type = typeof(ulong);
                    break;
                case "object":
                    type = typeof(object);
                    break;
                case "short":
                    type = typeof(short);
                    break;
                case "ushort":
                    type = typeof(ushort);
                    break;
                case "string":
                    type = typeof(string);
                    break;
                default:
                    {
                        // Suppose typeName is full name of class
                        type = GetTypeCore(typeName);
     
                        // Did not find the namespace to use all of the match again and again
                        if (type == null)
                        {
                            foreach (string theNamespace in namespaces)
                            {
                                type = GetTypeCore(string.Concat(theNamespace, ".", typeName));
     
                                // To find a qualified first class
                                if (type != null)
                                    break;
                            }
                        }
                    }
                    break;
            }
     
            if (isNullable && type != null)
                type = typeof(Nullable<>).MakeGenericType(type);
     
            return type;
        }
     
        private Type GetTypeCore(string typeName)
        {
            Type type = Type.GetType(typeName);
            if (type != null)
                return type;
     
            Assembly[] listAssembly = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly assembly in listAssembly)
            {
                type = assembly.GetType(typeName, false, false);
                if (type != null)
                    return type;
            }
     
            if (assemblyExtensions != null && assemblyExtensions.Any())
            {
                foreach (Assembly assembly in assemblyExtensions)
                {
                    type = assembly.GetType(typeName, false, false);
                    if (type != null)
                        return type;
                }
            }
     
            if (Assemblies != null && Assemblies.Any())
            {
                foreach (Assembly assembly in Assemblies)
                {
                    type = assembly.GetType(typeName, false, false);
                    if (type != null)
                        return type;
                }
            }
            return null;
        }
     
        private bool NextIsEqual(string symbol, bool throwExceptionIfError = true)
        {
            if (spResult.Next() != symbol)
            {
                if (throwExceptionIfError)
                    throw new ApplicationException(string.Format("{0} isn't the next token", symbol));
                else
                    return false;
            }
            return true;
        }
        #endregion
    }

    使用到的一个异常类如下:

    /// <summary>
    /// Parse UnfindType Exception
    /// </summary>
    [Serializable]
    [DebuggerStepThrough]
    public class ParseUnfindTypeException : Exception
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ParseUnfindTypeException"/> class.
        /// </summary>
        /// <param name="typeName">Name of the type.</param>
        /// <param name="errorIndex">Index of the error.</param>
        public ParseUnfindTypeException(string typeName, int errorIndex)
            : base(string.Format("{0} in the vicinity of the type \"{1}\" not found", errorIndex, typeName))
        {
        }
    }

    需要注意的是,这个类用到了上次提到的字符串解析结果,需要两者相互配合。嘿嘿,再给个示意图诱惑一下:

    未命名

    如果您感觉有用,顺手点下推荐,谢了!

  • 相关阅读:
    我的第一篇博客
    文献笔记5
    文献笔记4
    文献笔记8
    文献笔记6
    文献笔记10
    文献笔记7
    文献笔记1
    文献笔记2
    文献笔记3
  • 原文地址:https://www.cnblogs.com/lenic/p/2536156.html
Copyright © 2011-2022 走看看