反射不只是動態加載類型,更是構建可擴展架構的基石。本文呈現三個有實戰價值、複雜度適中的案例:配置驅動日誌實例化、插件系統、通用下拉綁定助手。
系統支援多種日誌輸出方式(文字檔、XML、資料庫),部署時透過修改配置文件切換,無需重編譯。
public interface ILog
{
bool Write(string message);
bool Write(Exception ex);
}
public class TextFileLog : ILog { /* 寫入 .txt */ }
public class XmlFileLog : ILog { /* 寫入 .xml */ }
<appSettings>
<add key="LogType" value="MyApp.TextFileLog" />
<add key="LogTarget" value="c:\log.txt" />
</appSettings>
Type type = Type.GetType(ConfigurationManager.AppSettings["LogType"]);
ILog log = (ILog)Activator.CreateInstance(type);
log.Write(new Exception("測試異常"));
string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LogLibrary.dll");
Assembly asm = Assembly.LoadFrom(dllPath);
Type type = asm.GetType(ConfigurationManager.AppSettings["LogType"]);
ILog log = (ILog)Activator.CreateInstance(type);
public interface IHost
{
List<ILog> Plugins { get; }
int LoadPlugins(string path);
ILog GetLog(string name);
}
public interface ILog
{
bool Write(string message);
bool Write(Exception ex);
}
public class Host : IHost
{
private List<ILog> _plugins = new List<ILog>();
public List<ILog> Plugins => _plugins;
public int LoadPlugins(string path)
{
foreach (var file in Directory.GetFiles(path, "*.dll"))
{
Assembly asm = Assembly.LoadFrom(file);
foreach (var type in asm.GetExportedTypes())
{
if (type.IsClass && typeof(ILog).IsAssignableFrom(type))
{
var plugin = (ILog)Activator.CreateInstance(type);
_plugins.Add(plugin);
}
}
}
return _plugins.Count;
}
public ILog GetLog(string name)
{
return _plugins.FirstOrDefault(p => p.GetType().FullName == name);
}
}
var host = new Host();
host.LoadPlugins("."); // 掃描當前目錄
var log = host.GetLog(ConfigurationManager.AppSettings["LogType"]);
log.Write(new Exception("插件測試"));
利用反射動態讀取對象屬性,消除重複的 DropDownList 綁定代碼。
public class DropDownHelper
{
private readonly ListControl _control;
public DropDownHelper(ListControl control) => _control = control;
// 核心:透過屬性名字串獲取值
public void Add<T>(T item, string textProp, string valueProp)
{
string text = (string)item.GetType()
.InvokeMember(textProp, BindingFlags.GetProperty, null, item, null);
string value = (string)item.GetType()
.InvokeMember(valueProp, BindingFlags.GetProperty, null, item, null);
_control.Items.Add(new ListItem(text, value));
}
public void BindTo<T>(IEnumerable<T> source, string textProp, string valueProp)
{
_control.Items.Clear();
foreach (var item in source) Add(item, textProp, valueProp);
}
public string SelectedValue
{
get => _control.SelectedValue;
set => _control.SelectedValue = value;
}
}
var helper = new DropDownHelper(ddlUsers);
helper.BindTo(userList, "UserName", "UserId"); // 任意集合、任意屬性名
| 案例 | 核心反射 API | 解耦點 |
|---|---|---|
| 配置驅動 | Type.GetType, Activator.CreateInstance | 具體類型從代碼移至配置 |
| 插件架構 | Assembly.LoadFrom, GetExportedTypes, IsAssignableFrom | 宿主不依賴具體插件程序集 |
| 通用綁定 | InvokeMember(GetProperty) | 綁定邏輯不依賴具體實體類 |
Type、PropertyInfo、MethodInfoDelegate.CreateDelegate 將反射轉為委托調用,接近直接調用速度ReflectionPermission反射讓「約定優於配置」落地,善用它能構建極具彈性的系統骨架。