在方法中转换不同的子类
Cast different subclasses in a method
我想发送、接收和投射 SkillEvent class 的子class,并能够根据其类型获取内部自定义参数。
可能有很多不同的子class具有不同的参数。
有没有办法避免为每种技能编写不同的方法?
class SkillEvent { float timestamp, EventType type }
class GrenadeSkillEvent : SkillEvent { Vector3 target_point}
[ClientRpc]
RpcSendSkillEvent(SkillEvent event){
eventManager.ExecuteEvent(event);
}
ExecuteEvent(SkillEvent event){
switch(event.type){
case GrenadeSkillEvent :
GrenadeSkillEvent grenadeEvent = (GrenadeSkillEvent) event;
float grenadeParam = event.grenadeParam;
break;
case OtherSkillEvent :
OtherSkillEvent otherEvent = (OtherSkillEvent ) event;
string stringParam = event.stringParam;
break;
}
}
我以为可以那样做,但显然不行。
有没有办法避免为每种技能编写不同的方法?
编辑:
GrenadeSkillEvent 和 OtherSkillEvent 是 SkillEvent 的子项。
它们都有时间戳和类型,我认为我可能需要帮助将事件变量转换为正确的 SkillEvent 子class。
我的问题是它们有相同的方法,比如说执行,但是每种子class需要不同类型的参数。
例如,GrenadeEvent 可能需要目标点、爆炸的大小和伤害。
一个BonusEvent可以给玩家增加一些HP。
A TerrainEvent 可以在玩家附近的重要物体上激活一些自定义动画,等等。
这些技能背后的逻辑都是一样的。
他们都有两种方法:
Simulate(SkillEvent Event) and Render(SkillEvent Event)
但是在 GrenadeSkill 中我需要它是
Simulate(GrenadeSkillEvent Event) and Render(GrenadeSkillEvent Event)
编辑 2 :
networkedPlayer.RpcOnSkillEvent(
new SkillEvent[] {
new GrenadeSkillEvent() {
timestamp = state.timestamp,
id = 0,
point = target
}
}
);
编辑 3:
var dictionary = new Dictionary<Type, Action<SkillEvent>> {
{
typeof(GrenadeSkillEvent), (SkillEvent e) => {
Debug.Log("GrenadeSkill OnConfirm point"+ ((GrenadeSkillEvent)e).point);
}
}
};
public override void OnConfirm(SkillEvent skillEvent) {
if (skillEvent == null) return;
Debug.Log(skillEvent.GetType());
dictionary[skillEvent.GetType()](skillEvent);
}
如果您使用 C# 7 并且 GrenadeSkillEvent
和 OtherSkillEvent
从 SkillEvent
派生,您可以使用模式匹配:
switch (@event) //@event is of SkillEvent type
{
case GrenadeSkillEvent gse:
float grenadeParam = gse.grenadeParam;
break;
case OtherSkillEvent ose:
string stringParam = ose.stringParam;
break;
case null:
//null handling
break;
default:
//other types
break;
}
如果没有,你可以使用字典:
var dictionary = new Dictionary<Type, Action<SkillEvent>>
{
{
typeof(GrenadeSkillEvent), e =>
{
float grenadeParam = ((GrenadeSkillEvent)e).grenadeParam;
//some logic
}
},
{
typeof(OtherSkillEvent), e =>
{
string stringParam = ((OtherSkillEvent)e).stringParam;
//some logic
}
}
}
if (@event == null)
{
//null handling
}
else
{
dictionary[@event.GetType()](@event);
}
或者,一种更面向对象的方法是在 SkillEvent
中创建一个抽象方法,并在所有具体 类 中覆盖它,但您需要具有通用的 return 类型和参数.
最好的方法是在 class、SkillEvent
的顶部写一个通用的 getter 方法,然后在 class 中重写这个方法是特殊的.由于您似乎需要不同的类型,我认为您可以使用一种方法来执行您在 class
中参数化的方法
这是一个包含您当前代码的示例:
class SkillEvent {
float timestamp;
EventType type ;
public virtual void executeEvent(){ //handles the general case for the execute funciton
execute(timestamp);
}
}
class GrenadeSkillEvent : SkillEvent { //overriden to pass target point into event execution
Vector3 target_point;
public override void executeEvent(){
execute(target_point);
}
}
class BonusEvent : SkillEvent {
int bonus_health;
public override void executeEvent(){/overriden to pass bonus health into event
execute(bonus_health);
}
}
class TerrainEvent : SkillEvent {
GameObject obj;
public override void executeEvent(){//overriden to play animation before event is executed
animation(obj);
execute();
}
}
[ClientRpc]
RpcSendSkillEvent(SkillEvent event){
eventManager.ExecuteEvent(event);
}
ExecuteEvent(SkillEvent event){
event.executeEvent();
}
简短的回答是,您可以通过制作自己的类型来完成您所要求的,但您真的不想那样做。这种代码正是多态性旨在防止的。要理解原因,假设您在代码中的 5 或 10 个不同位置使用了此模式——每个位置都有一个基于 Type 的 switch 语句,以及在每种情况下为 运行 的不同代码。现在假设您要引入一种新的 SkillEvent 类型:您必须找到代码中打开 Type 的每个地方并添加更多代码。您突然打开所有已经过 QA 的代码,并且必须再次对其进行 QA。另外,如果您忘记了其中一个景点怎么办?多么痛苦,不得不去编辑所有这些不同的地方只是为了添加一个新的具体实例 class.
现在考虑一下正确的做法:对于这 10 个地方中的每一个,您都在基础 class 中创建一个抽象方法,然后在每个具体的 class 中重写它.抽象方法创建一个契约,每个具体 class 都可以以自己的方式实现。
在您的具体示例中,您说要从不同的具体 classes 中提取不同的参数。你打算用这些参数做什么?是创建一个描述对象的字符串吗?是将这些参数传递给您即将进行的服务调用吗?想想你想要实现的目标。我假设是后一种情况,因为这是您实际需要单独参数的地方。所以,让你在基础 class 中定义的抽象方法成为
abstract Map<String, Object> getParameters();
或者
abstract void addParameters(Map<String, Object>);
现在,具体的 classes 创建一个映射并用它们自己的参数填充它,但是您的调用方法不必知道有关此方法实现的任何信息。这就是你希望 GrenadeSkillEvent
的知识所在的地方,无论如何,在 class 中。您不希望其他 class 知道 GrenadeSkillEvent
的任何细节。毕竟,您可能会决定在将来更改其实现,使其具有新参数。在这一点上,您不需要记住去寻找其中包含此开关的 callXYZServiceCall
代码。 (更糟糕的是,不是你添加了新的参数,而是项目中的另一个工程师,他甚至不知道要担心这个 switch 语句。)
Idk,这只是想法。 (不知道,我使用相同的方法)
也许好的解决方案是使用技能数据库,并发送带有字符串参数的 RPC 而不是可序列化 类(您的 SkillEvent
)
因此,使用这个技能数据库,您可以根据需要使用字符串参数执行代码。
你可以把技能id和参数放到字符串中,发送rpc,接收命令,解析字符串,通过id技能和参数执行代码。
- 将技能参数和技能 ID 放入字符串
- 使用此字符串发送 RPC
- 接收 RPC
- 将字符串解析为您的格式(在此示例中为 jsonObject)
- 通过
id_skill
查找使用的技能并使用参数执行代码
- 盈利!
解析或创建字符串的有用工具,将是一些 JSON 解析器(serializator..idk)
也许你会问我:"why strings and json?"
我回答你:"because it's flexible!"
示例:
void UseGrenade(){ // when some player using grenade skill.
JsonObject json = new JsonObject();
json.addField("id_skill", id); // id of grenade skill
json.addField("timestamp", 1.5f);
json.addField("position", targetPosition);
SendRpcSkill(json.Print()); // converting json to string and sending this string
}
例如执行手榴弹的参数:
// it is pseudo json for example
{
id_skill: 1, // id of grenade
timestamp: 1.5,
position: {1,21,3}
}
或其他一些技能:
// it is pseudo json for example
{
id_skill: 2, // id of some skill
timestamp: 1.8,
msg:"rain of fishes for cats!",
// ... and other params
}
并执行此技能:
[ClientRpc]
RpcSendSkill(string skillData){
JsonObject json = new JsonObject(skillData); //converting string to json
int idskill = json["id_skill"]; // get id of used skill
mySkillsDataBase[idskill].Execute(json); // find skill and execute code
}
你游戏中的某处:
public class GrenadeSkill: BaseSkill{
public override void Execute(JsonObject json){
float timeStamp = (int)json["timestamp"];
Vector3 targetPosition = (Vector3)json["position"];
// ...
// and you grenade logic
}
}
希望你能理解我的想法。
我想发送、接收和投射 SkillEvent class 的子class,并能够根据其类型获取内部自定义参数。
可能有很多不同的子class具有不同的参数。
有没有办法避免为每种技能编写不同的方法?
class SkillEvent { float timestamp, EventType type }
class GrenadeSkillEvent : SkillEvent { Vector3 target_point}
[ClientRpc]
RpcSendSkillEvent(SkillEvent event){
eventManager.ExecuteEvent(event);
}
ExecuteEvent(SkillEvent event){
switch(event.type){
case GrenadeSkillEvent :
GrenadeSkillEvent grenadeEvent = (GrenadeSkillEvent) event;
float grenadeParam = event.grenadeParam;
break;
case OtherSkillEvent :
OtherSkillEvent otherEvent = (OtherSkillEvent ) event;
string stringParam = event.stringParam;
break;
}
}
我以为可以那样做,但显然不行。
有没有办法避免为每种技能编写不同的方法?
编辑:
GrenadeSkillEvent 和 OtherSkillEvent 是 SkillEvent 的子项。
它们都有时间戳和类型,我认为我可能需要帮助将事件变量转换为正确的 SkillEvent 子class。
我的问题是它们有相同的方法,比如说执行,但是每种子class需要不同类型的参数。
例如,GrenadeEvent 可能需要目标点、爆炸的大小和伤害。
一个BonusEvent可以给玩家增加一些HP。
A TerrainEvent 可以在玩家附近的重要物体上激活一些自定义动画,等等。
这些技能背后的逻辑都是一样的。
他们都有两种方法:
Simulate(SkillEvent Event) and Render(SkillEvent Event)
但是在 GrenadeSkill 中我需要它是
Simulate(GrenadeSkillEvent Event) and Render(GrenadeSkillEvent Event)
编辑 2 :
networkedPlayer.RpcOnSkillEvent(
new SkillEvent[] {
new GrenadeSkillEvent() {
timestamp = state.timestamp,
id = 0,
point = target
}
}
);
编辑 3:
var dictionary = new Dictionary<Type, Action<SkillEvent>> {
{
typeof(GrenadeSkillEvent), (SkillEvent e) => {
Debug.Log("GrenadeSkill OnConfirm point"+ ((GrenadeSkillEvent)e).point);
}
}
};
public override void OnConfirm(SkillEvent skillEvent) {
if (skillEvent == null) return;
Debug.Log(skillEvent.GetType());
dictionary[skillEvent.GetType()](skillEvent);
}
如果您使用 C# 7 并且 GrenadeSkillEvent
和 OtherSkillEvent
从 SkillEvent
派生,您可以使用模式匹配:
switch (@event) //@event is of SkillEvent type
{
case GrenadeSkillEvent gse:
float grenadeParam = gse.grenadeParam;
break;
case OtherSkillEvent ose:
string stringParam = ose.stringParam;
break;
case null:
//null handling
break;
default:
//other types
break;
}
如果没有,你可以使用字典:
var dictionary = new Dictionary<Type, Action<SkillEvent>>
{
{
typeof(GrenadeSkillEvent), e =>
{
float grenadeParam = ((GrenadeSkillEvent)e).grenadeParam;
//some logic
}
},
{
typeof(OtherSkillEvent), e =>
{
string stringParam = ((OtherSkillEvent)e).stringParam;
//some logic
}
}
}
if (@event == null)
{
//null handling
}
else
{
dictionary[@event.GetType()](@event);
}
或者,一种更面向对象的方法是在 SkillEvent
中创建一个抽象方法,并在所有具体 类 中覆盖它,但您需要具有通用的 return 类型和参数.
最好的方法是在 class、SkillEvent
的顶部写一个通用的 getter 方法,然后在 class 中重写这个方法是特殊的.由于您似乎需要不同的类型,我认为您可以使用一种方法来执行您在 class
这是一个包含您当前代码的示例:
class SkillEvent {
float timestamp;
EventType type ;
public virtual void executeEvent(){ //handles the general case for the execute funciton
execute(timestamp);
}
}
class GrenadeSkillEvent : SkillEvent { //overriden to pass target point into event execution
Vector3 target_point;
public override void executeEvent(){
execute(target_point);
}
}
class BonusEvent : SkillEvent {
int bonus_health;
public override void executeEvent(){/overriden to pass bonus health into event
execute(bonus_health);
}
}
class TerrainEvent : SkillEvent {
GameObject obj;
public override void executeEvent(){//overriden to play animation before event is executed
animation(obj);
execute();
}
}
[ClientRpc]
RpcSendSkillEvent(SkillEvent event){
eventManager.ExecuteEvent(event);
}
ExecuteEvent(SkillEvent event){
event.executeEvent();
}
简短的回答是,您可以通过制作自己的类型来完成您所要求的,但您真的不想那样做。这种代码正是多态性旨在防止的。要理解原因,假设您在代码中的 5 或 10 个不同位置使用了此模式——每个位置都有一个基于 Type 的 switch 语句,以及在每种情况下为 运行 的不同代码。现在假设您要引入一种新的 SkillEvent 类型:您必须找到代码中打开 Type 的每个地方并添加更多代码。您突然打开所有已经过 QA 的代码,并且必须再次对其进行 QA。另外,如果您忘记了其中一个景点怎么办?多么痛苦,不得不去编辑所有这些不同的地方只是为了添加一个新的具体实例 class.
现在考虑一下正确的做法:对于这 10 个地方中的每一个,您都在基础 class 中创建一个抽象方法,然后在每个具体的 class 中重写它.抽象方法创建一个契约,每个具体 class 都可以以自己的方式实现。
在您的具体示例中,您说要从不同的具体 classes 中提取不同的参数。你打算用这些参数做什么?是创建一个描述对象的字符串吗?是将这些参数传递给您即将进行的服务调用吗?想想你想要实现的目标。我假设是后一种情况,因为这是您实际需要单独参数的地方。所以,让你在基础 class 中定义的抽象方法成为
abstract Map<String, Object> getParameters();
或者
abstract void addParameters(Map<String, Object>);
现在,具体的 classes 创建一个映射并用它们自己的参数填充它,但是您的调用方法不必知道有关此方法实现的任何信息。这就是你希望 GrenadeSkillEvent
的知识所在的地方,无论如何,在 class 中。您不希望其他 class 知道 GrenadeSkillEvent
的任何细节。毕竟,您可能会决定在将来更改其实现,使其具有新参数。在这一点上,您不需要记住去寻找其中包含此开关的 callXYZServiceCall
代码。 (更糟糕的是,不是你添加了新的参数,而是项目中的另一个工程师,他甚至不知道要担心这个 switch 语句。)
Idk,这只是想法。 (不知道,我使用相同的方法)
也许好的解决方案是使用技能数据库,并发送带有字符串参数的 RPC 而不是可序列化 类(您的 SkillEvent
)
因此,使用这个技能数据库,您可以根据需要使用字符串参数执行代码。
你可以把技能id和参数放到字符串中,发送rpc,接收命令,解析字符串,通过id技能和参数执行代码。
- 将技能参数和技能 ID 放入字符串
- 使用此字符串发送 RPC
- 接收 RPC
- 将字符串解析为您的格式(在此示例中为 jsonObject)
- 通过
id_skill
查找使用的技能并使用参数执行代码 - 盈利!
解析或创建字符串的有用工具,将是一些 JSON 解析器(serializator..idk)
也许你会问我:"why strings and json?"
我回答你:"because it's flexible!"
示例:
void UseGrenade(){ // when some player using grenade skill.
JsonObject json = new JsonObject();
json.addField("id_skill", id); // id of grenade skill
json.addField("timestamp", 1.5f);
json.addField("position", targetPosition);
SendRpcSkill(json.Print()); // converting json to string and sending this string
}
例如执行手榴弹的参数:
// it is pseudo json for example
{
id_skill: 1, // id of grenade
timestamp: 1.5,
position: {1,21,3}
}
或其他一些技能:
// it is pseudo json for example
{
id_skill: 2, // id of some skill
timestamp: 1.8,
msg:"rain of fishes for cats!",
// ... and other params
}
并执行此技能:
[ClientRpc]
RpcSendSkill(string skillData){
JsonObject json = new JsonObject(skillData); //converting string to json
int idskill = json["id_skill"]; // get id of used skill
mySkillsDataBase[idskill].Execute(json); // find skill and execute code
}
你游戏中的某处:
public class GrenadeSkill: BaseSkill{
public override void Execute(JsonObject json){
float timeStamp = (int)json["timestamp"];
Vector3 targetPosition = (Vector3)json["position"];
// ...
// and you grenade logic
}
}
希望你能理解我的想法。