首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

反射實戰三部曲:配置驅動實例化、插件架構、通用下拉綁定

     編輯:關於C#

反射的實用價值:從「能動」到「好用」

反射不只是動態加載類型,更是構建可擴展架構的基石。本文呈現三個有實戰價值、複雜度適中的案例:配置驅動日誌實例化、插件系統、通用下拉綁定助手。

案例一:配置文件驅動動態創建對象

場景

系統支援多種日誌輸出方式(文字檔、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);
}

宿主實現:掃描目錄下所有 DLL,尋找實現 ILog 的類並實例化

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 對象:頻繁反射調用前緩存 Type、PropertyInfo、MethodInfo
  • 優先用強類型委托:Delegate.CreateDelegate 將反射轉為委托調用,接近直接調用速度
  • 權限考量:部分信任環境 (如共享主機) 可能限制 ReflectionPermission

反射讓「約定優於配置」落地,善用它能構建極具彈性的系統骨架。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved