共享一个从字符串转 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))
    {
    }
}

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

未命名

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

原文地址:https://www.cnblogs.com/lenic/p/2536156.html