我在C#编写了这个,并且想到的最快解决方案是使用“as”或“is”关键字。我开始想知道如何在C ++(没有RTTI)中整齐地实现它......甚至在没有上述关键字的C#中也是如此。
这是问题(简化):
有一个类Command
,其中包含一组所谓的“令牌”。
class Command
{
public List<Token> Toks {get; set;}
//...
}
令牌可能(当前)是“关键字”或“游戏对象”。
class Token
{
//nothing in here :(
}
class KWToken : Token
{
public List<string> Synonyms {get; set;}
}
class GOToken : Token
{
}
稍后,我想循环遍历Command
对象的Token
列表,并根据存储的Token
类型执行操作。
这种情况下的棘手是KWToken
个对象包含我需要的List
个关联字符串。
当然,如前所述,C#中的简单解决方案将使用“is”或“as”关键字。
我想到了一些不那么性感的方法 任何人都有不错的想法?
编辑
(删除打印示例,因为它似乎具有误导性)
将通过关联的Command对象在许多地方访问KWToken对象中的同义词列表。不只是一次打印它们,因为我可能暗示了我的例子。
答案 0 :(得分:6)
您也可以尝试使用Visitor Pattern。虽然这对你的简单场景来说可能有点过头了。
答案 1 :(得分:4)
这是非常经典的多态性。以下工作不会吗?
abstract class Token
{
public abstract void DoStuff();
}
class KeywordToken : Token
{
List<string> Synonyms { get; set; }
public override void DoStuff()
{
foreach (string s in Synonyms)
{
Console.WriteLine(s);
}
}
}
class GameObjectToken : Token
{
public override void DoStuff()
{
// Do something else.
}
}
// Elsewhere
foreach (var token in cmd.Toks)
{
token.DoStuff();
}
答案 2 :(得分:1)
使用LINQ的OfType()仅提取列表中所需类型的元素:
foreach(var kw in command.Toks.OfType<KWToken>())
{
kw.Synonyms[...];
}
答案 3 :(得分:1)
正如其他人所建议的那样,多态性和访问者模式似乎很适合这个问题。
在C ++中,也可以使用诸如boost::variant
之类的类来一般地完成。
然后可以大致定义您的Command类:
class Command
{
public:
std::vector<boost::variant<KWToken, GOToken> > Toks;
//...
};
我们可以定义一个静态访问者类,定义要对每个对象类型执行的操作:
class my_visitor : public boost::static_visitor<void>
{
public:
// define the operation on KWTokens
void operator()(KWToken& tok) const
{
//...
// since tok is well typed, we can access its Synonyms member without any problems.
DoStuff(tok.Synonyms)
}
// and the operation on GOTokens
void operator()(GOToken& tok) const
{
//...
}
};
然后,如果变量v
包含其中一个令牌类型,您可以像这样调用访问者:
boost::apply_visitor( my_visitor(), v );
有关详细信息,请参阅http://www.boost.org/doc/libs/1_44_0/doc/html/variant.html。
这避免了“传统的”OOP多态性,为您提供了一个完全基于模板的解决方案(使用静态多态性)。
在这种方法中,您甚至不需要公共Token
基类,具体的令牌类型不需要实现任何虚函数。
在C#中,你真的没有这个选项,并且访问者模式(仍然是正确的解决方案)必须使用动态多态来实现。
答案 4 :(得分:0)
如果您不想使用RTTI(如果可能我建议您使用它),您可以实施它。
#include <string>
template <class T>
class List
{};
class Token
{
//nothing in here :(
public:
enum TokenType
{
KWToken,
GOToken
};
virtual TokenType Which() = 0;
};
class KWToken : public Token
{
public:
List<std::string> Synonyms() {
//get
return List<std::string>();
}
void Synonyms(List<std::string>& value) {
//set
}
virtual TokenType Which() {return Token::KWToken; }
};
class GOToken : public Token
{
virtual TokenType Which() {return Token::GOToken; }
};
int main() {
Token* T = GetToken();
switch(T->What()) {
case Token::KWToken:
//Do fancy stuff
static_cast<KWToken*>(T)->Synonyms();
break;
}
}
答案 5 :(得分:0)
Microsoft的COM架构可以优雅地处理这个问题。基类IUnknown仅包含获取接口所需的最少功能,接口定义了对象可以或不可以执行的操作。在您的情况下,您可以定义关键字列表的接口,如果该接口返回NULL,则该对象不支持关键字列表。你不需要实现COM本身,我只是以它为例。
您可以根据它的作用来定义对象,而不是它是什么。
答案 6 :(得分:0)
我对您的方案了解不多,但可能是添加方法的选项
virtual bool hasSynonyms() = 0;
virtual List<std::string> getSynomyms() { return List<std::string>; /*empty list */ }
到您的令牌基类。
只有在可能有其他令牌可能有同义词的情况下才会这样做,否则访客模式就是可行的方式。