如何将聊天命令实现为单独的类?
How to implement chat commands as separate classes?
我目前正在为 Twitch 频道开发聊天机器人,我希望程序中的所有命令都是独立的 classes,这样我只需要添加或删除一个 class 添加或删除命令。
我在互联网上搜索了很长时间,但一直没有找到合适的设计。下面是我认为它必须看起来像但找不到正确语法的内容。
class Command()
{
string keyword;
int globalCooldown;
List<string> args;
}
class DoStuffA : Command(List<string> _args)
{
keyword = "pleasedostuffa";
globalCooldown = 2;
args = _args;
DoStuff(List<string> args)
{
//doing stuff A here with all the logic and so on
}
}
class DoStuffB : Command(List<string> _args)
{
keyword = "pleasedostuffb";
globalCooldown = 8;
args = _args;
DoStuff(List<string> args)
{
//doing stuff B here with all the logic and so on
}
}
为什么我需要这个是因为我想在 List<Commands>
中存储所有可能的命令,当新的聊天消息出现时,搜索此列表中的哪个对象与 keyword
匹配聊天命令并执行相应的功能。比如有人发!pleasedostuffa
,我执行
foreach (Command c in commands)//commands is List<Command>
{
if(c.keyword==receivedCommand.command)//.command is string
{
c.DoStuff(receivedCommand.argsAsList)//.argsAsList is List<string>
}
}
我希望我能正确解释这一点,并且真的很想至少知道这是如何实现的。
提前致谢!
您的方法设置几乎正确,但还需要进行一些其他更改。您需要让基础 class 将 DoStuff() 公开为虚拟方法。试试这个:
public abstract class Command
{
public string keyword;
public int globalCooldown;
//List<string> args;
public abstract void DoStuff(List<string> args);
}
public class DoStuffA : Command
{
//public string keyword = "pleasedostuffa";
//public int globalCooldown = 2;
//args = _args;
public DoStuffA()
{
keyword = "pleasedostuffa";
globalCooldown = 2;
}
public override void DoStuff(List<string> args)
{
//doing stuff A here with all the logic and so on
}
}
public class DoStuffB : Command
{
//public string keyword = "pleasedostuffb";
//public int globalCooldown = 8;
// args = _args;
public DoStuffB()
{
keyword = "pleasedostuffb";
globalCooldown = 8;
}
public override void DoStuff(List<string> args)
{
//doing stuff B here with all the logic and so on
}
}
那么,一些注意事项。
方法继承
这里我将基础 class 抽象化,只是为了强制每个 child 命令实现抽象函数 DoStuff()
。毕竟,您将如何处理基 class 的实例?它不会做任何事情,因为您没有实际的实现。因此抽象有助于避免意外实例化 Command 本身,并确保 sub-type 实现者做正确的事。
其次,在childclass层面,需要override
基础class的方法。这确保任何调用 ((Command)doStuffB).DoStuff()
的东西都能正确实现函数。
现在您在 Command 上有了一个 DoStuff() 方法,您的 foreach
循环应该会按预期工作。您在基础 class 上有可用的方法,因此 child 级别的虚拟覆盖可以是 运行 而无需强制转换。
基础class成员访问
您要在此处声明的字段,keyword
和 globalCooldown
,通常不是人们公开此类信息的方式,但在我们开始之前,我将解释一下从继承的 classes.
访问 base-class 成员的更基本原则
这两个字段需要标记为 public
(并指定适当的类型),以便它们可以在 class 之外(在您的 foreach 中)使用。 public
关键字称为可访问性修饰符,还有一些其他可访问性选项,但在您的情况下,只有 public
可能会执行您想要的操作。
如您所见,我已经注释掉了 child class 中的字段。如果您在那里声明它们,它们将隐藏(但不会覆盖)基 class 上的同名成员。对于字段,没有 virtual
或 abstract
的等价物,因此您需要另一种策略。在这里,我们将您对这些字段的原始声明保留在基础 class 上,以便它们可用于任何持有任何类型命令的对象。但是,我们没有在 child class 级别重新声明它们,而是简单地在 child class 的构造函数中设置基本 class 成员的值。
请注意,为了清楚起见,您可以使用 base.keyword = "etc";
明确指定要在基础 class 上设置成员。
通过属性公开内部值
正如我指出的那样,这会起作用,但大多数人并不完全会公开 keyword
和 globalCooldown
值。为此,您通常会改用 属性。这使您可以存储和公开值,而不必冒让他人更改值(有意或无意)的风险。在这种情况下,您希望以这种方式声明 属性:
public string Keyword // properties start with a capital letter by convention
{
get; // The get accessor is public, same as the overall property
protected set; // the set accessor is protected
}
protected
set 访问器意味着它仍然可以被 child classes 访问,但不能被其他任何人访问。这可能就是你想要的。所以现在,在您的 child 构造函数中,您可以设置 base.Keyword = "whatever";
并且您的 foreach
代码可以引用但不能覆盖该值。您可以用类似的方式声明 GlobalCooldown。
我目前正在为 Twitch 频道开发聊天机器人,我希望程序中的所有命令都是独立的 classes,这样我只需要添加或删除一个 class 添加或删除命令。
我在互联网上搜索了很长时间,但一直没有找到合适的设计。下面是我认为它必须看起来像但找不到正确语法的内容。
class Command()
{
string keyword;
int globalCooldown;
List<string> args;
}
class DoStuffA : Command(List<string> _args)
{
keyword = "pleasedostuffa";
globalCooldown = 2;
args = _args;
DoStuff(List<string> args)
{
//doing stuff A here with all the logic and so on
}
}
class DoStuffB : Command(List<string> _args)
{
keyword = "pleasedostuffb";
globalCooldown = 8;
args = _args;
DoStuff(List<string> args)
{
//doing stuff B here with all the logic and so on
}
}
为什么我需要这个是因为我想在 List<Commands>
中存储所有可能的命令,当新的聊天消息出现时,搜索此列表中的哪个对象与 keyword
匹配聊天命令并执行相应的功能。比如有人发!pleasedostuffa
,我执行
foreach (Command c in commands)//commands is List<Command>
{
if(c.keyword==receivedCommand.command)//.command is string
{
c.DoStuff(receivedCommand.argsAsList)//.argsAsList is List<string>
}
}
我希望我能正确解释这一点,并且真的很想至少知道这是如何实现的。
提前致谢!
您的方法设置几乎正确,但还需要进行一些其他更改。您需要让基础 class 将 DoStuff() 公开为虚拟方法。试试这个:
public abstract class Command
{
public string keyword;
public int globalCooldown;
//List<string> args;
public abstract void DoStuff(List<string> args);
}
public class DoStuffA : Command
{
//public string keyword = "pleasedostuffa";
//public int globalCooldown = 2;
//args = _args;
public DoStuffA()
{
keyword = "pleasedostuffa";
globalCooldown = 2;
}
public override void DoStuff(List<string> args)
{
//doing stuff A here with all the logic and so on
}
}
public class DoStuffB : Command
{
//public string keyword = "pleasedostuffb";
//public int globalCooldown = 8;
// args = _args;
public DoStuffB()
{
keyword = "pleasedostuffb";
globalCooldown = 8;
}
public override void DoStuff(List<string> args)
{
//doing stuff B here with all the logic and so on
}
}
那么,一些注意事项。
方法继承
这里我将基础 class 抽象化,只是为了强制每个 child 命令实现抽象函数 DoStuff()
。毕竟,您将如何处理基 class 的实例?它不会做任何事情,因为您没有实际的实现。因此抽象有助于避免意外实例化 Command 本身,并确保 sub-type 实现者做正确的事。
其次,在childclass层面,需要override
基础class的方法。这确保任何调用 ((Command)doStuffB).DoStuff()
的东西都能正确实现函数。
现在您在 Command 上有了一个 DoStuff() 方法,您的 foreach
循环应该会按预期工作。您在基础 class 上有可用的方法,因此 child 级别的虚拟覆盖可以是 运行 而无需强制转换。
基础class成员访问
您要在此处声明的字段,keyword
和 globalCooldown
,通常不是人们公开此类信息的方式,但在我们开始之前,我将解释一下从继承的 classes.
这两个字段需要标记为 public
(并指定适当的类型),以便它们可以在 class 之外(在您的 foreach 中)使用。 public
关键字称为可访问性修饰符,还有一些其他可访问性选项,但在您的情况下,只有 public
可能会执行您想要的操作。
如您所见,我已经注释掉了 child class 中的字段。如果您在那里声明它们,它们将隐藏(但不会覆盖)基 class 上的同名成员。对于字段,没有 virtual
或 abstract
的等价物,因此您需要另一种策略。在这里,我们将您对这些字段的原始声明保留在基础 class 上,以便它们可用于任何持有任何类型命令的对象。但是,我们没有在 child class 级别重新声明它们,而是简单地在 child class 的构造函数中设置基本 class 成员的值。
请注意,为了清楚起见,您可以使用 base.keyword = "etc";
明确指定要在基础 class 上设置成员。
通过属性公开内部值
正如我指出的那样,这会起作用,但大多数人并不完全会公开 keyword
和 globalCooldown
值。为此,您通常会改用 属性。这使您可以存储和公开值,而不必冒让他人更改值(有意或无意)的风险。在这种情况下,您希望以这种方式声明 属性:
public string Keyword // properties start with a capital letter by convention
{
get; // The get accessor is public, same as the overall property
protected set; // the set accessor is protected
}
protected
set 访问器意味着它仍然可以被 child classes 访问,但不能被其他任何人访问。这可能就是你想要的。所以现在,在您的 child 构造函数中,您可以设置 base.Keyword = "whatever";
并且您的 foreach
代码可以引用但不能覆盖该值。您可以用类似的方式声明 GlobalCooldown。