尋找AppDomain中所有組件中的衍生Type

因為希望某一個相關的程式寫好,不用做任何的設定就可以直接使用,這個需求我使用搜尋整個AppDomain下的Type,只要符合我自定的Interface或Base Class,就可以運作,這方法雖然有點取巧,但還滿好用的

因為希望某一個相關的程式寫好,不用做任何的設定就可以直接使用,這個需求我使用搜尋整個AppDomain下的Type,只要符合我自定的Interface或Base Class,就可以運作,這方法雖然有點取巧,但還滿好用的,缺點第一次會比較慢,且沒辦法停用。

 

範例

public interface IValidateProvider
{
    bool CanVaild(Type type);

    bool Vaild(object model);
}

 

例如我有一個IValidateProvider的介面,專案為某種情況下做驗證,我希望我實作一個Class就可以直接啟用這一個Provider,不用做其他的設定。

public bool IsValid(object model)
{
    if (model == null)
    {
        throw new ArgumentNullException("model");
    }

    if (providers == null)
    {
        //先快取下一次不用在執行
        providers = new List<IValidateProvider>();
        //找出所有實作IValidateProvider的類別
        foreach (var type in ReflectionHelper.FindDerivedTypes(typeof(IValidateProvider)))
        {
            if (type.IsInterface)
            {
                continue;
            }

            //Type前題要沒有建構值,不然會出錯
            providers.Add((IValidateProvider)Activator.CreateInstance(type));
        }
    }

    bool result = true;
    Type modeType = model.GetType();
    foreach (var validator in providers)
    {
        if (validator.CanVaild(modeType))
        {
            result &= validator.Vaild(model);
        }

        if (!result)
        {
            break;
        }
    }

    return result;
}

 

 

程式說明

1.首先要先取得所有的組件,我是用AppDomain.CurrentDomain.GetAssemblies,這個Method來取得所有的組件,AppDomain.CurrentDomain.GetAssemblies這個Method不光只是取得專案中所參考的組件,連目前執行路徑下的其他組件都會取得到,例如專案中我參考了三個組件,但我在exe的同一層目錄下放了其他七個組件,用AppDomain.CurrentDomain.GetAssemblies會取得十個組件。

/// <summary>
/// 取得所需組件
/// </summary>
/// <param name="containGAC">是否要包含GAC組件</param>
/// <param name="finder">過濾函式</param>
/// <returns></returns>
public static IEnumerable<Assembly> GetAssemblies(bool containGAC = false, Func<Assembly, bool> finder = null)
{
    var app = AppDomain.CurrentDomain;
    var dynamicDirectory = app.SetupInformation.CachePath ?? string.Empty;
    var list = new List<Assembly>();
    foreach (var assembly in app.GetAssemblies())
    {
        if (assembly.IsDynamic || !assembly.Location.Contains(dynamicDirectory))
        {
            //因為有可能會ShadowCopy
            continue;
        }

        if (!containGAC && assembly.GlobalAssemblyCache)
        {
            continue;
        }

        if (finder != null && !finder(assembly))
        {
            continue;
        }

        list.Add(assembly);
    }

    return list;
}

NOTE:

用AppDomain.CurrentDomain.GetAssemblies()也會連ShadowCopy的組件一併取出,所以如果有設定ShadowCopy的話,如ASP.NET預設會設定,同一個組件會被取出二次,但有個麻煩的地方,雖然是同一個組件,但同一個Type與ShadowCopy的Type比較是不等於的,所以當有使用ShadowCopy,會濾掉原路徑的組件(因為Runtime中所有Type都是ShadowCopy的組件中的)。

 

2.把取出的組件的所有Type取出,使用Type.IsAssignableFrom 比對。

/// <summary>
/// 尋找所有衍生Type
/// </summary>
/// <param name="baseType">基礎Type</param>
/// <param name="containGAC">是否要包含GAC組件</param>
/// <param name="findNonPublic">是否要包含私有Type</param>
/// <returns></returns>
public static IEnumerable<Type> FindDerivedTypes(Type baseType, bool containGAC = false, bool findNonPublic = false)
{
    lock (DerivedTypes)
    {
        IEnumerable<Type> derived;
        if (!DerivedTypes.TryGetValue(baseType, out derived))
        {
            var list = new List<Type>();
            //因為自己寫的組件都不會放在GAC中,所以預設會過濾掉GAC的組件,加快搜尋速度
            foreach (var assembly in GetAssemblies(containGAC))
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (type == baseType)
                    {
                        continue;
                    }

                    if (type.IsNotPublic || type.IsNestedPrivate)
                    {
                        if (!findNonPublic)
                        {
                            continue;
                        }
                    }

                    if (baseType.IsAssignableFrom(type))
                    {
                        list.Add(type);
                    }
                }
            }
            derived = list;
            DerivedTypes.Add(baseType, list);
        }

        return derived;
    }
}

 

下載範例程式