简体   繁体   English

C#泛型或反射 - 调用未知类的函数

[英]C# Generics or Reflection - calling a function of unknown class

I'm making a game where workers perform actions based on a current Task . 我正在制作一个游戏,工作人员根据当前Task执行操作。 Each worker will be assigned a list of tasks, in a preferred order (which is influenced by the player's decisions). 每个工作人员将按优先顺序(受玩家决定影响)分配任务列表。

When a task is completed (eg take item from X to Y), the worker needs to start a new task by checking through their list of possible tasks, see if each can be performed, and if so, set their current task to that task and start it (the last task - "Wander Around" is always going to be available). 当任务完成时(例如,将项目从X转到Y),工作人员需要通过检查可能的任务列表来启动新任务,看看是否可以执行每项任务,如果是,则将当前任务设置为该任务并开始它(最后一个任务 - “漫游”总是可用)。

I currently have this working using a big switch statement and Enums, but now want to generalise this code to create a Task class, and give the workers a list of preferred Tasks, a GetNextTask() function, and in the worker's Update() method, call currentTask.update() (this will get the worker to do whatever he's required to do under the current task, and which will call worker.GetNextTask() when the task is complete). 我目前使用一个大的switch语句和Enums工作,但现在想要概括这个代码来创建一个Task类,并为worker提供一个首选任务列表,一个GetNextTask()函数,并在worker的Update()方法中,调用currentTask.update() (这将使工作人员在当前任务下执行他需要做的任何事情,并在任务完成时调用worker.GetNextTask() )。

What I'm unclear on is the best way to store Tasks in the worker. 我不清楚的是在工作中存储任务的最佳方法。 Should I use: 我应该使用:

1) Reflection. 1)反思。 Store the possible Tasks as a list of types, then use reflection to a) call a static method public static virtual bool CanPerformThisTask() which is overridden in each subclass, and b) Create an instance of that task for the worker? 将可能的任务存储为类型列表,然后使用反射来a)调用静态方法public static virtual bool CanPerformThisTask()在每个子类中被覆盖,以及b)为worker创建该任务的实例? (example attempt at code for this below - but unable to test yet) (示例尝试下面的代码 - 但无法测试)

2) Instantiate all the Tasks whenever a worker needs to get a new task (probably using Activator), and check (Task)task.CanPerformThisTask() for each one - if true, do that task. 2)每当工作人员需要获取新任务(可能使用Activator)时,实例化所有任务,并检查(Task)task.CanPerformThisTask()每个任务 - 如果为true,则执行该任务。 Instantiating them all seems inefficient though? 实例化它们似乎都效率低下了吗?

3) Generics. 3)泛型。 Can this be done using generics? 这可以使用泛型来完成吗? If so, how? 如果是这样,怎么样?

Here is a snippet of my classes to give the idea of what I'm trying to do: 这是我的课程片段,用于说明我正在尝试做的事情:

Worker Class: 工人阶级:

protected List<Point> waypoints = new List<Point>();
public bool reachedDestination { get { return waypoints.Count == 0; } }
protected Task task;
public List<Type> possibleTasks;

public Worker(Task initialTask, List<Type> initialPossibleTasks ...)
: base(...)
{
     task = initialTask;
     possibleTasks = initialPossibleTasks;
}

public override void Update()
{
     base.Update();
     if (!reachedDestination) Move();
     task.Update();
}

public void GetNextTask()
{
    foreach (Type t in possibleTasks)
    {
        //reflection code here - will this work and can we do this with generics instead?
        Bool canDoT = (bool)t.GetMethod("CanPerformThisTask", BindingFlags.Static | BindingFlags.Public).Invoke(null, null);
        if (canDoT)
        {
            task = Activator.CreateInstance(t);
            return;
        }
    }
}

Here is some incomplete code for my base Task class (which shouldn't be instantiated): 这是我的基本Task类的一些不完整的代码(不应该实例化):

public class Task
{
    public Worker worker;

    public virtual static bool CanPerformThisTask()
    {
        //never call this from here - always from subclasses
        return false;
    }

    public Task()
    {
        //set up code here
    }

    public virtual void Update()
    {
        //make worker do relevant activities here
        //call finish task when done
    }

    public void FinishTask()
    {
        worker.GetNextTask();
    }

}

and here is an example of a Task the worker will have in its list of possible tasks: 以下是工作人员在其可能任务列表中将具有的任务示例:

public class T_WorkerWander : Task
{

    public static override bool CanPerformThisTask()
    {
        //can always wander (other Tasks will have conditions here)
        return true;
    }

    public T_WorkerWander()
        : base()
    {
    }

    override public void Update()
    {
        //make the worker wander here
        if (worker.reachedDestination) FinishTask();
    }

}

Update: here is the code I've now got working 更新:这是我现在正在使用的代码

Task Class: 任务类:

public abstract class Task
{
    //the entity holding this task
    public TaskableEntity taskEntity;

    public List<TaskStage> taskStages;
    public TaskStage currentTaskStage { get { return taskStages[0]; } }

    public Task(TaskableEntity t) { taskEntity = t; }

    /// <summary>
    /// the conditions for the Task to be started
    /// </summary>
    public virtual bool CanStart()
    {
        return true;
    }

    public void Start()
    {
        taskStages = new List<TaskStage>();
        InitialiseTaskStages();
        taskStages[0].Start();
    }

    public abstract void InitialiseTaskStages();

    public void Update()
    {
        currentTaskStage.Update();
        if (currentTaskStage.IsComplete()) TaskStageComplete();
    }

    public void TaskStageComplete()
    {
        taskStages.RemoveAt(0);
        if (taskStages.Count == 0) taskEntity.TaskComplete();
        else currentTaskStage.Start();
    }

    public void SetTaskStages(params TaskStage[] t)
    {
        taskStages = t.ToList();
    }

    public void Interrupt()
    {
        currentTaskStage.Interrupt();
    }

}

TaskStage class: TaskStage类:

public sealed class TaskStage
{
    private Task task;

    private List<Point> pointsToMoveTo;
    public void SetPointsToMoveTo(Point p) { pointsToMoveTo = new List<Point>() { p }; }
    public void SetPointsToMoveTo(params Point[] p) { pointsToMoveTo = p.ToList(); }
    public void SetPointsToMoveTo(List<Point> p) { pointsToMoveTo = p; }
    public Action actionToApply;

    private float timeToWait;
    public void SetWait(float wait) { timeToWait = wait; }

    private IReservable[] itemsToReserve;
    public void SetItemsToReserve(params IReservable[] items) { itemsToReserve = items; }

    private IReservable[] itemsToUnreserve;
    public void SetItemsToUnreserve(params IReservable[] items) { itemsToUnreserve = items; }

    private Emotion emotionToSet;
    public void SetEmotionToSet(Emotion e) { emotionToSet = e; }

    private TaskStage _interrupt;
    public void SetInterruptAction(TaskStage t) { _interrupt = t; }
    public void Interrupt() { _interrupt.Start(); }

    public TaskStage(Task t)
    {
        task = t;
    }

    public void Start()
    {
        if (actionToApply != null) actionToApply();
        if (itemsToUnreserve != null) UnreserveItems();
        if (itemsToReserve != null) ReserveItems();
        if (pointsToMoveTo != null)
        {
            //this will need changing after pathfinding sorted out...
            if (pointsToMoveTo.Count == 1) task.taskEntity.SetWaypoints(pointsToMoveTo[0]);
            else task.taskEntity.waypoints = pointsToMoveTo;
        }
        if (emotionToSet != null) emotionToSet.StartEmotion();
    }

    public void Update()
    {
        if (timeToWait > 0) timeToWait -= GV.elapsedTime;
    }

    public bool IsComplete()
    {
        if (pointsToMoveTo != null && !task.taskEntity.reachedDestination) return false;
        if (timeToWait > 0) return false;
        return true;
    }

    public void ReserveItems()
    {
        foreach (IReservable i in itemsToReserve)
        {
            i.reserved = true;
        }
    }

    public void UnreserveItems()
    {
        foreach (IReservable i in itemsToUnreserve)
        {
            i.reserved = false;
        }
    }

}

Example Task: 示例任务:

public class T_WorkerGoToBed : Task
{
    public FactoryWorker worker { get { return taskEntity as FactoryWorker; } }
    public T_WorkerGoToBed(TaskableEntity t)
        : base(t) { }

    public override bool CanStart()
    {
        return Room.Available<Bed>(GV.Bedrooms);
    }

    public override void InitialiseTaskStages()
    {
        Bed bedToSleepIn = Room.NearestAvailableFurniture<Bed>(GV.Bedrooms, taskEntity.X, taskEntity.Y);

        //stage 1 - reserve bed and move there
        TaskStage ts1 = new TaskStage(this);
        ts1.SetItemsToReserve(bedToSleepIn);
        ts1.SetPointsToMoveTo(bedToSleepIn.XY);

        //stage 2 - sleep in bed
        TaskStage ts2 = new TaskStage(this);
        ts2.SetWait((worker.maxEnergy - worker.energy) / worker.energyRegeneratedPerSecondWhenSleeping);
        ts2.SetEmotionToSet(new E_Sleeping(worker, false));

        //stage 3 - unreserve bed
        TaskStage ts3 = new TaskStage(this);
        ts3.SetItemsToUnreserve(bedToSleepIn);
        ts3.SetEmotionToSet(new E_Happy(worker, false));

        SetTaskStages(ts1, ts2, ts3);
    }

}

It sounds like you need to reverse responsibility between task and worker. 听起来你需要在任务和工人之间扭转责任。 Instead of asking whether the task can be performed, ask the worker if he can perform a given task: 不要询问是否可以执行任务,而是询问工人是否可以执行给定任务:

class Worker
{
    bool CanPerformTask<T>() where T : Task
    {
        var type = typeof(T);
        // code to determine whether worker can perform the task T             
    }

    // alternative with instance parameter
    bool CanPerformTask<T>( T task ) where T : Task
    {
        // code to determine whether worker can perform the task passed in
    }
}

This solution avoids the "instantiate all tasks or call static method" problem. 此解决方案避免了“实例化所有任务或调用静态方法”问题。

Also, consider using the built-in collection classes. 另外,请考虑使用内置集合类。 Things such as queues and stacks can greatly simplify the code needed to schedule execution of things. 诸如队列和堆栈之类的东西可以大大简化安排执行事务所需的代码。

I think you are abusing the point of static classes. 我认为你正在滥用静态类的观点。 The "Task" class should be standard (not static). “任务”类应该是标准的(不是静态的)。 Your "Worker" class is not static therefore implying that there is more than one "Worker" instance. 您的“Worker”类不是静态的,因此暗示有多个“Worker”实例。 Given this paradigm, these workers can probably have the same task assigned to them. 鉴于这种范例,这些工人可能可以分配给他们相同的任务。

Your "Worker" class needs to have this property modified from: 您的“Worker”类需要修改此属性:

public List possibleTasks; public list possibleTasks;

to

public List _possibleTasks; public List _possibleTasks;

You probably should not have public access to this property either. 您可能也不应该公开访问此属性。 You can modify "CanPerformThisTask" as necessary. 您可以根据需要修改“CanPerformThisTask”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM