共享一個從字符串轉 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))
    {
    }
}

 

需要注意的是,這個類用到了上次提到的字符串解析結果,需要兩者相互配合。嘿嘿,再給個示意圖誘惑一下:

未命名

 

如果您感覺有用,順手點下推薦,謝了!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM