首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C# 反射實現動態創建對象並調用方法:完整框架設計指南

     編輯:程式設計綜合問答

需求場景

UI 界面提供兩個下拉框:第一個選擇類類型 (A/B),第二個動態顯示該類的公共方法列表,用戶選擇方法後點擊執行,實現插件式調用架構。

核心實現:反射三步走

1. 獲取類型

// 假設類在當前程序集中
Type type = Type.GetType("MyNamespace." + className); // 如 "MyNamespace.ClassA"
// 或從程序集查找
// Type type = Assembly.GetExecutingAssembly().GetTypes().First(t => t.Name == className);

2. 創建實例

object instance = Activator.CreateInstance(type); // 無參構造函數
// 有參構造函數
// object instance = Activator.CreateInstance(type, new object[] { arg1, arg2 });

3. 獲取並調用方法

// 獲取所有公共實例方法
MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
// 綁定到第二個下拉框
comboMethods.DataSource = methods;
comboMethods.DisplayMember = "Name";

// 執行選中的方法
MethodInfo method = (MethodInfo)comboMethods.SelectedItem;
object result = method.Invoke(instance, null); // 無參方法
// 有參方法
// object result = method.Invoke(instance, new object[] { param1, param2 });

完整可運行示例 (WinForms)

using System;
using System.Reflection;
using System.Windows.Forms;

namespace DynamicInvokeDemo
{
    public class ClassA { public void MethodX() => MessageBox.Show("A.X"); public void MethodY(int n) => MessageBox.Show($"A.Y: {n}"); }
    public class ClassB { public void MethodJ() => MessageBox.Show("B.J"); public string MethodK(string s) => $"B.K: {s}"; }

    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
            // 初始化類下拉框
            cmbClass.Items.AddRange(new[] { "ClassA", "ClassB" });
            cmbClass.SelectedIndexChanged += CmbClass_SelectedIndexChanged;
            btnInvoke.Click += BtnInvoke_Click;
        }

        private void CmbClass_SelectedIndexChanged(object sender, EventArgs e)
        {
            string className = cmbClass.SelectedItem.ToString();
            Type type = Type.GetType($"DynamicInvokeDemo.{className}");
            if (type == null) return;

            // 獲取公共實例方法 (排除 Object 繼承方法)
            var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            cmbMethod.DataSource = methods;
            cmbMethod.DisplayMember = "Name";
        }

        private void BtnInvoke_Click(object sender, EventArgs e)
        {
            if (cmbClass.SelectedItem == null || cmbMethod.SelectedItem == null) return;

            try
            {
                string className = cmbClass.SelectedItem.ToString();
                Type type = Type.GetType($"DynamicInvokeDemo.{className}");
                object instance = Activator.CreateInstance(type);
                MethodInfo method = (MethodInfo)cmbMethod.SelectedItem;

                // 簡單參數處理:演示用,實際需根據 ParameterInfo 動態構建 UI
                object[] parameters = null;
                if (method.GetParameters().Length > 0)
                {
                    // 這裡簡單處理:彈窗輸入
                    string input = Microsoft.VisualBasic.Interaction.InputBox("請輸入參數 (多個用逗號分隔):", "參數輸入");
                    parameters = input.Split(',').Select(s => s.Trim()).ToArray();
                }

                object result = method.Invoke(instance, parameters);
                if (result != null)
                    MessageBox.Show($"執行結果: {result}");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"調用失敗: {ex.Message}");
            }
        }
    }
}

進階架構設計:插件系統最佳實踐

1. 定義插件契約接口

public interface IPlugin
{
    string Name { get; }
    string Description { get; }
    void Execute(PluginContext context);
}

public class PluginContext
{
    public Dictionary Parameters { get; } = new();
    public IServiceProvider Services { get; set; }
}

2. 特性標記與自動發現


[AttributeUsage(AttributeTargets.Class)]
public class PluginAttribute : Attribute
{
    public string Category { get; set; }
    public int Priority { get; set; }
}

[Plugin(Category = "DataProcess", Priority = 1)]
public class ExportExcelPlugin : IPlugin { ... }

3. 插件管理器


public class PluginManager
{
    private readonly List _plugins = new();
    
    public void LoadPlugins(string pluginDirectory)
    {
        foreach (var dll in Directory.GetFiles(pluginDirectory, "*.dll"))
        {
            var asm = Assembly.LoadFrom(dll);
            foreach (var type in asm.GetTypes())
            {
                if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface)
                {
                    var plugin = (IPlugin)Activator.CreateInstance(type);
                    _plugins.Add(plugin);
                }
            }
        }
        _plugins.Sort((a,b) => {
            var attrA = a.GetType().GetCustomAttribute();
            var attrB = b.GetType().GetCustomAttribute();
            return (attrB?.Priority ?? 0).CompareTo(attrA?.Priority ?? 0);
        });
    }
    
    public IReadOnlyList Plugins => _plugins.AsReadOnly();
}

性能優化關鍵點

問題解決方案
頻繁反射調用慢緩存 MethodInfo/ConstructorInfo;使用 Delegate.CreateDelegate 生成強類型委托
表達式樹編譯將反射調用封裝為 `Func` 委托,首次編譯後接近直接調用速度
動態方法使用 System.Reflection.Emit.DynamicMethod 生成 IL 代碼,繞過訪問檢查

安全性考量

  • ⚠️ 反射可繞過訪問修飾符 (`BindingFlags.NonPublic`),需嚴格驗證輸入類型/方法白名單
  • ⚠️ 插件加載來自外部 DLL,建議啟用 Assembly.LoadFrom 簽名驗證或運行在沙箱/AppDomain 隔離
  • ⚠️ 避免在高頻熱點路徑使用反射,改用源生成器或接口編程
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved