提问人:stackedAE 提问时间:12/2/2020 更新时间:12/9/2020 访问量:195
C# - 映射输入对象以类型安全的方式影响对象
C# - Mapping input objects to effect objects in a typesafe way
问:
我试图解决的问题在概念上非常简单。我将用游戏来解释它,但我相信这个概念适用于任何具有输入、效果关系的对象:
技能由输入对象列表和效果对象列表组成。输入对象定义用户交互的类型(如选择地面图块或选择单位),每个输入对象将从用户那里获得某种类型的目标(例如图块或单位)。效果对象定义对特定类型目标的效果(例如,移动到地面图块或损坏单位)。
在代码中使用能力包括:
- 按顺序提示用户输入对象(等待用户完成每个对象的输入,然后再继续下一个)
- 按顺序执行效果(每个效果将从映射到的输入对象获取数据)
因此,定义能力的一个例子可能是:
Ability: {
Inputs: {
Select Tile,
Select Unit
}
Effects: {
Move to (input 1),
Deal 10 damage (input 2)
}
}
理想情况下,我想使从输入到效果类型的映射是安全的,因为每种类型的效果都有它期望的目标数据类型(例如图块或单元)。 下面是我编写的代码示例,用于表示代码中的操作、输入和效果对象:
public class AbilityData
{
List<InputData<Target>> inputs;
List<AbilityEffect> effects;
public void exampleCreate()
{
BaseInputData<Tile> input = new TileSelectData();
inputs.Add(input);
effect = new List<AbilityEffect>();
BaseAbilityEffect<Tile> effect = new BaseAbilityEffect<Tile>(new TileEffect(), input);
effects.Add(effect);
}
public void exampleRun()
{
foreach (InputData<AbilityInput> input in inputs)
{
input.promptInput();
}
foreach (AbilityEffect effect in effects)
{
effect.execute();
}
}
}
public interface AbilityEffect
{
void execute();
}
public class BaseAbilityEffect<T> : AbilityEffect where T : Target
{
InputData<T> input;
TargetEffect<T> effect;
public BaseAbilityEffect(TargetEffect<T> tEffect, InputData<T> input) {
this.input = input;
this.effect = tEffect;
}
public void execute()
{
effect.execute(input.getInput());
}
}
public class TargetEffect<T> where T : Target
{
public virtual void execute(T input) { }
}
public class UnitEffect : TargetEffect<Unit>
{
public override void execute(Unit unit)
{
// Do something on the unit
}
}
public class TileEffect : TargetEffect<Tile>
{
public override void execute(Tile tile)
{
// Do something on the tile
}
}
public interface InputData<out T>
{
void promptInput();
T getInput();
}
public abstract class BaseInputData<T> : InputData<T> where T : Target
{
public abstract T getInput();
public abstract void promptInput();
}
public class TileSelectData : BaseInputData<Tile>
{
public override Tile getInput()
{
// Return the stored input
}
public override void promptInput()
{
// prompt for the input and store it.
}
}
public class UnitSelectData : BaseInputData<Unit>
{
public override Unit getInput()
{
// Return the stored input
}
public override void promptInput()
{
// prompt for the input and store it.
}
}
这似乎在正常的本地情况下可以正常工作,但是当您需要为输入提供覆盖时,问题就出现了。例如,在联网游戏中,客户端将触发输入,然后将目标发送到主服务器。然后,主服务器需要覆盖输入对象以使其接收到目标,然后调用效果。
所以我想添加类似的东西
void overrideInput(T);
添加到 InputData 接口,但由于它使用协方差,因此我不能将泛型类型参数用作任何接口函数的参数。
有没有办法绕过这个限制?这个概念看起来非常简单:确保效果对象仅与相同目标类型的输入对象匹配。当然,我可以通过一堆不安全的铸件以蛮力完成此任务,但似乎应该有更好的方法。
答:
0赞
Siderite Zackwehdex
12/9/2020
#1
我通过添加另一种类型 Tinput 来编译您的代码。我不知道这是否是你想要的:
public class AbilityData
{
List<InputData<Target,Target>> inputs;
List<AbilityEffect> effects;
public void exampleCreate()
{
BaseInputData<Tile,Target> input = new TileSelectData();
inputs.Add(input);
var effects = new List<AbilityEffect>();
var effect = new BaseAbilityEffect<Tile,Target>(new TileEffect(), input);
effects.Add(effect);
}
public void exampleRun()
{
foreach (InputData<AbilityInput, AbilityInput> input in inputs)
{
input.promptInput();
}
foreach (AbilityEffect effect in effects)
{
effect.execute();
}
}
}
public interface AbilityEffect
{
void execute();
}
public class BaseAbilityEffect<T,Tinput> : AbilityEffect
where T : Target,Tinput
{
InputData<T,Tinput> input;
TargetEffect<T> effect;
public BaseAbilityEffect(TargetEffect<T> tEffect, InputData<T,Tinput> input)
{
this.input = input;
this.effect = tEffect;
}
public void execute()
{
effect.execute(input.getInput());
}
}
public class TargetEffect<T> where T : Target
{
public virtual void execute(T input) { }
}
public class UnitEffect : TargetEffect<Unit>
{
public override void execute(Unit unit)
{
// Do something on the unit
}
}
public class TileEffect : TargetEffect<Tile>
{
public override void execute(Tile tile)
{
// Do something on the tile
}
}
public interface InputData<out T,Tinput>
where T: Tinput
{
void promptInput();
T getInput();
void overrideInput(Tinput input);
}
public class Target
{
}
public class Tile : Target
{
}
public class TileSelectData : BaseInputData<Tile,Target>
{
public TileSelectData()
{
}
}
public class Unit : Target
{
}
public class AbilityInput
{
}
public class BaseInputData<T,Tinput> : InputData<T,Tinput>
where T:Tinput
{
public T getInput()
{
throw new NotImplementedException();
}
public void overrideInput(Tinput input)
{
throw new NotImplementedException();
}
public void promptInput()
{
throw new NotImplementedException();
}
}
评论
0赞
stackedAE
12/9/2020
对不起,代码被修改以使其更通用和易于理解(编译的原始代码)。更改的问题在于,现在在 InputData 中,您必须定义两种类型,out T 和 Tinput,但实际上它们应该始终是相同的类型(InputData 只处理一种类型的 Target)。
评论