因為希望某一個相關的程式寫好,不用做任何的設定就可以直接使用,這個需求我使用搜尋整個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;
}
}