如何在不使用RTTI的情况下(优雅地)实现这一点?

时间:2010-10-12 13:32:07

标签: c# c++ polymorphism rtti

我在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对象中的同义词列表。不只是一次打印它们,因为我可能暗示了我的例子。

7 个答案:

答案 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 */ }

到您的令牌基类。

只有在可能有其他令牌可能有同义词的情况下才会这样做,否则访客模式就是可行的方式。