如何创建可以采用多个参数的通用 class

How to create a generic class that can take many parameters

好的,我正在创建一个 Utility AI 框架。为此,我需要一个 class,它可以根据我确定的情况发生很大变化,我希望有一种方法可以使用多态性或某种设计模式来解决我的问题。

让我告诉你我的意思

为了举例,我有一个动作 假设我有以下动作 攻击目标

此操作可能有许多注意事项,这些注意事项会有很大差异,但都实现相同的接口:

   public interface IConsideration
{
    /// <summary>
    ///   A unique named identifier for this consideration.
    /// </summary>
    string NameId { get; }

    /// <summary>
    ///   The weight of this consideration.
    /// </summary>
    float Weight { get; set; }

    /// <summary>
    ///   If true, then the output of the associated evaluator is inverted, in effect, inverting the
    ///   consideration.
    /// </summary>
    bool IsInverted { get; set; }

    /// <summary>Calculates the utility given the specified context.</summary>
    /// <param name="context">The context.</param>
    /// <param name="value"></param>
    /// <returns>The utility.</returns>
    float Consider<T>(BaseAiContext context, T value);

}

以上是我目前的实现,并没有真正解决我遇到的问题

如您所见,此接口“最”重要的方法是 Consider

这就是问题所在,我最好能够以我可以控制的方式将数据传递给此 class。

举例来说,我考虑的一个因素是移动到位置,我想在这里发送以下参数:

以上只是举个例子来证明我的观点。这还有另一个问题——当我终于拥有正确的参数时,我如何才能传递正确的参数?假设我有以下代码:

public List<IConsideration> considerations;
float targetDistance = 2;

for (int i = 0; i < considerations.Count; i++)
{
    float AxisScore = considerations[i].Consider(BaseAiContext,targetDistance );
}

因为我必须使用 Interface 类型,所以我无法确切知道要将哪些值解析为参数。

总结一下:

如何以通用方式“参数化”我的 class?

如何区分这些参数化,以便提供正确值的考虑?

正如@MarcRasmussen 要求的那样

由于您的接口的每个实现可能使用不同的参数集,解决它的一种方法是像字典一样具有键值存储。

有很多改进要做,例如,使用 ENUMS 而不是字符串,并有一个静态管理器 class 用于 add/modify/remove 设置。

这是一个简单的例子,未经测试,信息可用。


public class MoveToTarget : IConsideration
{

   //method is changed to have generic return type and accept dictionary for settings
   float Consider(BaseAiContext context, Dictionary<string,object> settings){
     
      //make sure required keys exist  

      if(!settings.ContainsKey("DESTINATION"))
          throw new ApplicationException("Missing key DESTINATION");
      if(!settings.ContainsKey("SPEED"))
          throw new ApplicationException("Missing key SPEED");

         // retrieve you required settings, at this stage since you cast an object, you should check the type ... this problem would be solved if you have (as further below mentioned) a specific settings class for all your implementations. this way you ensure type safety too.

         Point destination =  (Point)settings["DESTINATION"];
         float speed = (float)settings["SPEED"];

         // and perform whatever logic you need. etc.
   }

}

public List<IConsideration> considerations;

//this should be probably static and globally available (?) probably better to have a singleton manager class to deal with that.

Dictionary<string,object> Settings = new Dictionary<string,object>()
{ 
   {"SPEED", 1.0f},
   {"RANDOM", new Random()},
   {"DESTINATION", new Point()},
   {"XYZ", "XYZ"},
   //etc.
}

//use foreach unless you need to have access to the index
foreach(var consideration in considerations)
{
    float AxisScore = consideration.Consider(BaseAiContext, Settings);
}

其他一些改进可能是为您的每个实现设置特定设置 class,例如 MoveToTargetSettings,然后您可以通过以下方式检索特定设置,而不是在字典中使用模棱两可的“KEYS”它的 class 等 var settings = settingDictionary["MoveToTargetSettins"] as MoveToTargetSettings

我认为需要更多细节才能更好,很高兴讨论和回答 SO 之外的任何其他问题,因为这将是题外话:)