You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何向UserManager传递自定义类类型并创建其实例?

解决自定义命令类的实例化与关联问题

嘿,你已经摸到了核心方向,只是在泛型实例化和类型传递这步有点卡壳——我来帮你把思路理顺,给出可运行的方案!

首先修正UserManager的泛型实现

你之前的泛型尝试思路是对的,但有几个关键问题:

  • 泛型参数需要类型约束,确保传入的T必须是UserClass的子类,并且能被实例化
  • 不能直接在非泛型类级别声明List<T>,可以用基类UserClass统一存储实例,或者把UserManager本身做成泛型类

方案1:把UserManager做成泛型类(适合单一命令类场景)

这种方式最直接,强制UserManager只处理一种自定义命令类:

public class UserInfo
{
    public int id;
    public UserInfo(int _id) => id = _id;
}

// 基类保持不变
public class UserClass
{
    public UserInfo userInfo;
}

// 泛型UserManager,约束T必须是UserClass子类且有无参构造
public class UserManager<T> where T : UserClass, new()
{
    private List<UserInfo> _userInfos;
    public List<T> ChildInstances { get; private set; } = new List<T>();

    public UserManager(List<UserInfo> userInfos)
    {
        _userInfos = userInfos;
        SetupChildInstances();
    }

    private void SetupChildInstances()
    {
        foreach (var userInfo in _userInfos)
        {
            // 创建子类实例并关联UserInfo
            T child = new T();
            child.userInfo = userInfo;
            ChildInstances.Add(child);
        }
    }
}

方案2:非泛型UserManager支持多类型(适合多命令类场景)

如果你的库需要支持用户创建多种自定义命令类,可以用List<UserClass>存储,通过泛型方法动态创建:

public class UserManager
{
    private List<UserInfo> _userInfos;
    public List<UserClass> AllChildInstances { get; private set; } = new List<UserClass>();

    public UserManager(List<UserInfo> userInfos)
    {
        _userInfos = userInfos;
    }

    // 泛型方法,创建指定类型的子类实例并关联UserInfo(无参构造)
    public void RegisterChildClass<T>() where T : UserClass, new()
    {
        foreach (var userInfo in _userInfos)
        {
            T child = new T();
            child.userInfo = userInfo;
            AllChildInstances.Add(child);
        }
    }

    // 支持带参数构造函数的版本
    public void RegisterChildClassWithConstructor<T>(params object[] extraArgs) where T : UserClass
    {
        foreach (var userInfo in _userInfos)
        {
            // 把userInfo作为第一个构造参数,后续传入用户自定义参数
            T child = (T)Activator.CreateInstance(typeof(T), userInfo, extraArgs);
            AllChildInstances.Add(child);
        }
    }
}

然后实现[Command]属性的自动调用逻辑

你提到了[Command]属性,这需要用反射来扫描方法并存储,当收到命令时自动触发:

首先定义Command属性:

[AttributeUsage(AttributeTargets.Method)]
public class CommandAttribute : Attribute
{
    // 可选:允许用户自定义命令名称
    public string CommandName { get; }
    public CommandAttribute(string commandName = null) => CommandName = commandName;
}

然后在UserManager里添加命令扫描和调用的逻辑(以非泛型版本为例):

public class UserManager
{
    // 存储命令映射:命令名称 -> (实例, 方法)
    private Dictionary<string, (UserClass Instance, MethodInfo Method)> _commandMap = new Dictionary<string, (UserClass, MethodInfo)>();

    public void RegisterChildClass<T>() where T : UserClass, new()
    {
        foreach (var userInfo in _userInfos)
        {
            T child = new T();
            child.userInfo = userInfo;
            AllChildInstances.Add(child);
            
            // 扫描当前实例的Command方法
            ScanForCommands(child);
        }
    }

    private void ScanForCommands(UserClass instance)
    {
        var methods = instance.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
            .Where(m => m.GetCustomAttribute<CommandAttribute>() != null);

        foreach (var method in methods)
        {
            var attr = method.GetCustomAttribute<CommandAttribute>();
            // 优先用用户自定义的命令名,否则用方法名
            string commandName = attr.CommandName ?? method.Name.ToLower();
            _commandMap[commandName] = (instance, method);
        }
    }

    // 调用命令的方法(从Socket收到命令后触发)
    public bool ExecuteCommand(string commandName, int userId, params object[] args)
    {
        // 根据userId找到对应用户的命令实例
        var targetInstance = AllChildInstances.FirstOrDefault(c => c.userInfo.id == userId);
        if (targetInstance == null) return false;

        if (_commandMap.TryGetValue(commandName.ToLower(), out var commandData) && commandData.Instance == targetInstance)
        {
            try
            {
                commandData.Method.Invoke(targetInstance, args);
                return true;
            }
            catch (Exception ex)
            {
                // 处理调用错误(比如参数不匹配)
                Console.WriteLine($"Command execution failed: {ex.Message}");
                return false;
            }
        }
        return false;
    }
}

用户侧的代码调整(以非泛型UserManager为例)

用户的自定义类和管理器可以这样写:

public class ChildClass : UserClass
{
    private CustomManager _customManager;

    // 带参数的构造函数,接收UserInfo和CustomManager
    public ChildClass(UserInfo userInfo, CustomManager customManager)
    {
        this.userInfo = userInfo;
        _customManager = customManager;
    }

    [Command] // 用方法名作为命令名
    public void Message(string _message)
    {
        _customManager.ReceiveMessage(userInfo.id, _message);
    }
}

public class CustomManager
{
    private UserManager _userManager;

    public CustomManager(List<UserInfo> userInfos)
    {
        _userManager = new UserManager(userInfos);
        // 注册自定义类,传入当前CustomManager作为构造参数
        _userManager.RegisterChildClassWithConstructor<ChildClass>(this);
    }

    public void ReceiveMessage(int _id, string _message)
    {
        Console.WriteLine($"We got message: {_message} from user with id: {_id}");
    }
}

额外优化建议

  • 缓存反射结果:如果服务器性能要求高,可以在程序启动时缓存所有带[Command]属性的方法,避免每次实例化都扫描
  • 依赖注入:可以引入简单的DI容器来管理CustomManager等依赖,避免手动传递构造参数
  • 命令参数校验:在ExecuteCommand里添加参数类型匹配的校验,避免调用出错
  • 抽象基类增强:如果需要强制子类必须实现某些逻辑,可以把UserClass改成抽象类,定义抽象方法

内容的提问来源于stack exchange,提问作者Bizio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:06:43