如何创建可以采用多个参数的通用 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 之外的任何其他问题,因为这将是题外话:)
好的,我正在创建一个 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 之外的任何其他问题,因为这将是题外话:)