从以下位置进行:


又一个C ++ Json Parser
又一个C ++ Json Parser(递归)

所有代码都可以从git hub获得: ThorsSerializer,但这里只回顾一小部分。

想法是,使用此库应该很容易定义如何序列化类而无需编写任何代码。请参阅README中的示例

#ifndef THORSANVIL_SERIALIZE_JSON_SERIALIZE_H
#define THORSANVIL_SERIALIZE_JSON_SERIALIZE_H

/* Content:
 *
 * Used to define how a class is imported/exported via the Json Serialized
 *
 * Conventions:
 *      T:      The type of the base class
 *      I:      The type of the member
 *      MP:     The type of the member pointer
 *
 *      S:      Source type (std::ostream output) (ThorsAnvil::Json::ScannerSax input)
 *
 * Every type that is serializeable has a class JsonSerializeTraits.
 * For basic types (int/float etc) no definition is required and the default template version works
 * For compound types you need to define the type SerializeInfo.
 *      SerializeInfo:           It is a mpl vector of types.
 *                              Each type (in the vector) defines how to serialize
 *                              a member of the compound type.
 *
 * Boilerplate  code to create the appropriate types for SerializeInfo.
 *      #define THORSANVIL_SERIALIZE_JsonAttribute(className, member)
 *
 * Example:
 *      class MyClass
 *      {
 *          // STUFF
 *
 *          // If any members are private and need to be serialized then
 *          // JsonSerializeTraits<MyClass> must be a friend of the class so it can generate the appropriate code
 *
 *          friend class JsonSerializeTraits<MyClass>;
 *      };
 *
 *      namespace ThorsAnvil { namespace Serialize { namespace Json {
 *      template<>
 *      class JsonSerializeTraits<MyClass>
 *      {
 *          static ThorsAnvil::Serialize::Json::JsonSerializeType const  type    = Map;
 *
 *          THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member1);
 *          THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member2);
 *          THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member3);
 *          typedef boost::mps::vector<member1, member2, member3>   SerializeInfo;
 *      };
 *      }}}
 *
 * Now we can serialize/deserialize with:
 *      std::cout << jsonExport(myClassObj) << "\n";
 *      std::cin  >> jsonInport(myClassObj);
 *
 * Same for std::vector
 *      std::vector<MyClass>  vec;  // Will serialize any fundamental type or type with JsonSerializeTraits<> specialized for it
 *                                  // If JsonSerializeTraits<> is not specialized for a compound type you get a compile time
 *                                  // error
 *      std::cout << jsonExport(vec) << "\n";
 *      std::cin  >> jsonImport(vec);
 */
#include "json/ScannerSax.h"
#include "json/ParserShiftReduce.tab.hpp"
#include <boost/mpl/at.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/type_traits/is_fundamental.hpp>
#include <boost/typeof/typeof.hpp>
#include <iostream>


/*
 * Helper Macros:
 * 
 * These are macros that will build some boilerplate types needed by the serialization code.
 *
 * THORSANVIL_SERIALIZE_JsonAttribute:          This is the main macro used.
 *                                              It identifies a class member that will be serialized
 *
 * THORSANVIL_SERIALIZE_JsonAttribute_1:        Used internally (should probably not be used by others).
 * THORSANVIL_SERIALIZE_JsonAttributeAccess:    If you want to run some code to as part of the serialization processes
 *                                              this macro allows you to specify a type that will be used during serialization.
 *                                              Examples will be provided in the documentaion.
 *
 * THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess:  A generic accessor can be used to generate multiple items.
 *                                                      When de-serializing the Json can be applied to multiple elements.
 *                                                      Used manly for container classes like std::map
 *THORSANVIL_SERIALIZE_JsonGenericArrAttributeAccess:   A generic accessor used by for arrays rather than maps (std::vector)
 *                                                      But otherwise identical to THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess
 */
#define THORSANVIL_SERIALIZE_JsonAttribute(className, member)                                                                           \
    typedef BOOST_TYPEOF(((className*)01)->member)    JsonAttribute ## member ## Type;                                                  \
    THORSANVIL_SERIALIZE_JsonAttribute_1(className, member, JsonSerializeTraits<JsonAttribute ## member ## Type>)

#define THORSANVIL_SERIALIZE_JsonAttribute_1(className, member, SerTraits)                                                              \
    typedef BOOST_TYPEOF(&className::member)            JsonAttribute ## member ## TypePtr;                                             \
    typedef JsonSerialElementAccessor<className, JsonAttribute ## member ## TypePtr, SerTraits>  JsonAttribute ## member ## Accessor;   \
    struct member: JsonSerializeItem<className, JsonAttribute ## member ## Accessor, std::string>                                       \
    {                                                                                                                                   \
        member()                                                                                                                        \
            : JsonSerializeItem<className, JsonAttribute ## member ## Accessor, std::string>(#member, &className::member)               \
        {}                                                                                                                              \
    }
#define THORSANVIL_SERIALIZE_JsonAttributeAccess(className, member, accessor)                                                           \
    struct member: JsonSerializeItem<className, accessor, std::string>                                                                  \
    {                                                                                                                                   \
        member()                                                                                                                        \
            : JsonSerializeItem<className, accessor, std::string>(#member, accessor())                                                  \
        {}                                                                                                                              \
    }
#define THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess(className, accessor)                                                         \
    struct genericAccessor: JsonSerializeItem<className, accessor, std::string>                                                         \
    {                                                                                                                                   \
        genericAccessor()                                                                                                               \
            : JsonSerializeItem<className, accessor, int>(-1 , accessor())                                                              \
        {}                                                                                                                              \
    }



namespace ThorsAnvil
{
    namespace Serialize
    {
        /* External dependencies from the generic Serialization code */
        template<typename T, typename Parser>
        struct Importer;

        template<typename T, typename Printer>
        struct Exporter;

        namespace Json
        {

/* Three basic element types:   Invalid (this obejct is not a top level JSON object)
 *                              Map     A JSON object   { [<string> : <value> [, <string> : <value>]*]? }
 *                              Array   A JSON array    [ [<value> [, <value>]*]? ]
 */
enum JsonSerializeType {Invalid, Map, Array};

/*
 * All objects that want to be serialized by this code must define their own specialization of this class.
 * The default version will cause compilation errors. Which hopefully will bring the reader here.
 */
template<typename T>
struct JsonSerializeTraits
{
    static JsonSerializeType const  type    = Invalid;
    typedef T                     SerializeInfo;
};

// Forward declarations
template<typename T, typename A, typename RegisterKey>
struct JsonSerializeItem;

/* Class used by boost::mpl::for_each. Nothing special simple lamba's will replace them in the future */
/*
 *  T       The object Type
 *  S       The source type (parser or std::ostream)
 *
 * This is basically trying to templatize and remove the need for multiple action objects that
 * are called from mpl::for_each
 */
template<typename T, typename S>
struct MPLForEachActivateTrait;

template<typename T>
struct MPLForEachActivateTrait<T, std::ostream>
{
    typedef const T                 ObjectType;
    static int const                SerializeActionIndex    = 0;
};
template<typename T>
struct MPLForEachActivateTrait<T, ThorsAnvil::Json::ScannerSax>
{
    typedef T                       ObjectType;
    static int const                SerializeActionIndex    = 1;
};
template<typename T, typename S>
class MPLForEachActivateItem
{
    typedef          MPLForEachActivateTrait<T, S>  Traits;
    typedef typename Traits::ObjectType             ObjectType;

    S&              pump;
    ObjectType&     object;
    public:
        MPLForEachActivateItem(S& p, ObjectType& obj)
            : pump(p)
            , object(obj)
        {}
        // Depending on if the pump is a stream or a scanner
        // Calls      JsonSerialize::activate()
        // or         JsonDeSerialize::activate()
        template<typename SerializeItem>
        void operator()(SerializeItem const& item) const
        {
            typedef typename boost::mpl::at_c<typename SerializeItem::SerializeType, Traits::SerializeActionIndex>::type  SerializeAction;
            SerializeAction::activate(item, pump, object);
        }
};

/*
 * Objects of this type get stored in the
 *      JsonSerializeTraits::SerializeInfo
 *  This is what the user create with the macros above. The default A is JsonSerialElementAccessor
 *  But user can define their own action for complex objects This wrapper is merely a vehicle for
 *  calling the A methods in a controlled manner.
 *
 * Note: These classes are not designed to be used directly but via the macros:
 *          THORSANVIL_SERIALIZE_JsonAttribute
 *          THORSANVIL_SERIALIZE_JsonAttributeAccess
 *  See the notes by these macros for details
 */
template<typename T, typename A, typename RegisterKey, JsonSerializeType type = JsonSerializeTraits<T>::type>
struct JsonSerialize;


template<typename T, typename A, typename RegisterKey>
struct JsonSerialize<T, A, RegisterKey, Map>
{
    // Generic serialization of a JSON object
    static void activate(JsonSerializeItem<T, A, RegisterKey> const& item, std::ostream& stream, T const& src)
    {
        if (!item.first)
        {   stream << ',';
        }
        stream << '"' << item.memberName << '"' << ":";
        item.accessor.serialize(src, stream);
    }
};
template<typename C, typename A, typename RegisterKey>
struct JsonSerialize<C, A, RegisterKey, Array>
{
    // Generic serialization of a JSON array
    static void activate(JsonSerializeItem<C, A, RegisterKey> const& item, std::ostream& stream, C const& src)
    {
        if (!item.first)
        {   stream << ',';
        }
        item.accessor.serialize(src, stream);
    }
};

template<typename T, typename A,typename RegisterKey, JsonSerializeType type = JsonSerializeTraits<T>::type>
struct JsonDeSerialize;

template<typename T, typename A,typename RegisterKey>
struct JsonDeSerialize<T, A, RegisterKey, Map>
{
    static void activate(JsonSerializeItem<T, A, RegisterKey> const& item, ThorsAnvil::Json::ScannerSax& parser, T& dst)
    {
        std::auto_ptr<ThorsAnvil::Json::SaxAction>    action(item.accessor.action(dst));
        parser.registerAction(item.memberName, action);
    }
};
template<typename T, typename A>
struct JsonDeSerialize<T, A, int, Array>
{
    static void activate(JsonSerializeItem<T, A, int> const& item, ThorsAnvil::Json::ScannerSax& parser, T& dst)
    {
        std::auto_ptr<ThorsAnvil::Json::SaxAction>    action(item.accessor.action(dst));
        parser.registerActionOnAllArrItems(action);
    }
};

template<typename T, typename A>
struct JsonDeSerialize<T, A, std::string, Array>
{
    static void activate(JsonSerializeItem<T, A, std::string> const& item, ThorsAnvil::Json::ScannerSax& parser, T& dst)
    {
        std::auto_ptr<ThorsAnvil::Json::SaxAction>    action(item.accessor.action(dst));
        parser.registerActionNext(action);
    }
};

/*
 * A type holder object that picks up the correct versions of JsonSerialize and JsonDeSerialize
 * Used by MPLForEachActivateItem to get the correct type
 */
template<typename T, typename A, typename RegisterKey>
struct JsonSerializeItem
{
    RegisterKey memberName;
    A           accessor;
    bool        first;

    JsonSerializeItem(RegisterKey const& name, A const& ac): memberName(name), accessor(ac), first(false) {}
    JsonSerializeItem& makeFirst() {first = true;return *this;}

    typedef    JsonSerialize<T,A,RegisterKey>       Serialize;
    typedef    JsonDeSerialize<T,A,RegisterKey>     DeSerialize;

    typedef boost::mpl::vector<Serialize, DeSerialize>      SerializeType;
};
/*
 * Importing
 * ============
 *
 * The JsonImportAction defines a class that is registered with a Json SAX parser
 * so that we can register callback actions for particular attributes.
 *
 * For fundamental types json is read directly into the value.
 * For compound types when the attribute is reached additional callbacks are registered
 * for each of the compound members that needs to be de-serialized (this is done recursively)
 * So we can de-serialize arbitrary json structures.
 */
template<typename SerializeInfo, typename I, bool EnablePod = boost::is_fundamental<I>::value>
class JsonImportAction: public ThorsAnvil::Json::SaxAction
{
    I&              memberRef;
    public:
        JsonImportAction(I& mr)
            : memberRef(mr)
        {}

        virtual void doPreAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&){}
        virtual void doAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&, JsonValue const& value)
        {
            // Read fundamental type directly into the member
            memberRef   = value.getValue<I>();
        }
};
template<typename SerializeInfo, typename I>
class JsonImportAction<SerializeInfo, I, false>: public ThorsAnvil::Json::SaxAction
{
    I&              memberRef;
    public:
        JsonImportAction(I& mr)
            : memberRef(mr)
        {}

        virtual void doAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&, JsonValue const&){}
        virtual void doPreAction(ThorsAnvil::Json::ScannerSax& parser, ThorsAnvil::Json::Key const&)
        {
            // Compound type. Register callback for each member.
            //                This is done when the attribute is reached in json not before
            boost::mpl::for_each<SerializeInfo>(MPLForEachActivateItem<I, ThorsAnvil::Json::ScannerSax>(parser, memberRef));
        }
};
/*
 * Need a function template to create the correct JsonImportAction()
 */
template<typename SerializeInfo, typename T, typename I>
ThorsAnvil::Json::SaxAction* new_JsonImportAction(T& dst, I T::* memberPtr)
{
    return new JsonImportAction<SerializeInfo, I>(dst.*memberPtr);
}


/* Default Serialization Traits:
 * Used by all types without their own specific serialization traits.
 */
template<JsonSerializeType>
struct JsonSerializeBrace
{
    static char braces[];
};

/*
 * The MemberScanner is used to register callbacks that will read sub-members from the json object
 */
template<typename T, typename MemberToSerialize = typename JsonSerializeTraits<T>::SerializeInfo>
struct MemberScanner
{
    void operator()(ThorsAnvil::Json::ScannerSax& scanner, T& destination)
    {
        boost::mpl::for_each<typename JsonSerializeTraits<T>::SerializeInfo>(MPLForEachActivateItem<T, ThorsAnvil::Json::ScannerSax>(scanner, destination));
    }
};
template<typename T>
struct MemberScanner<T, T>
{
    // A normal type with no SerializeInfo has no members thus no need to register callbacks.
    void operator()(ThorsAnvil::Json::ScannerSax& scanner, T& destination)
    {}
};
template<typename T>
struct MemberScanner<T, void>
{
    // A normal type with no SerializeInfo has no members thus no need to register callbacks.
    void operator()(ThorsAnvil::Json::ScannerSax& scanner, T& destination)
    {}
};

/*
 * The MemberPrinter prints each member of an object.
 */
template<typename T, typename MemberToSerialize = typename JsonSerializeTraits<T>::SerializeInfo>
struct MemberPrinter
{
    void operator()(std::ostream& stream, T const& source)
    {
        typedef typename boost::mpl::at<typename JsonSerializeTraits<T>::SerializeInfo, boost::integral_constant<int,0> >::type  FirstType;
        typedef typename boost::mpl::pop_front<typename JsonSerializeTraits<T>::SerializeInfo>::type                             AllButFirstType;

        MPLForEachActivateItem<T, std::ostream>     itemPrinter(stream, source);


        // Print the first member (Call makeFirst() means no comma is printed)
        itemPrinter(FirstType().makeFirst());

        // For each of the other members use a loop a proceed each object with a comma
        boost::mpl::for_each<AllButFirstType>(itemPrinter);
    }
};
template<typename T>
struct MemberPrinter<T, T>
{
    // A normal object just prints itself.
    void operator()(std::ostream& stream, T const& source)
    {
        stream << source;
    }
};
template<typename T>
struct MemberPrinter<T, void>
{
    void operator()(std::ostream& stream, T const& source)
    {}
};


struct JsonSerializer
{
    template<typename T, JsonSerializeType base = JsonSerializeTraits<T>::type>
    struct Parser: ThorsAnvil::Json::ScannerSax
    {
        typedef boost::mpl::bool_<base != Invalid>      Parsable;
        Parser(T& dst)
            : destination(dst)
        {
            MemberScanner<T>    memberScanner;
            memberScanner(*this, destination);
        }

        void parse(std::istream& stream)
        {
            ScannerSax::parse<yy::ParserShiftReduce>(stream);
        }

        T&  destination;
    };

    template<typename T ,JsonSerializeType base = JsonSerializeTraits<T>::type>
    struct Printer
    {
        typedef boost::mpl::bool_<base != Invalid>      Printable;
        Printer(T const& src)
            : source(src)
        {}

        void print(std::ostream& stream)
        {
            stream << JsonSerializeBrace<base>::braces[0];

            MemberPrinter<T>    memberPrinter;
            memberPrinter(stream, source);

            stream << JsonSerializeBrace<base>::braces[1];
        }

        T const&    source;
    };

};

/*
 * Default accessors for fundamental types std::string
 * The serialize()  Recursively calls jsonInternalExport() on the member to serialize the member.
 *
 * The action()     Creates a JsonImportAction() that is registered with the SAX parser that just reads the
 *                  Item directly into the object. If the object is a compound type it uses the SerializeInfo
 *                  to register subsequent actions recursively so we always read directly into an object
 *                  not a copy.
 */
template<typename T, typename MP, typename SerTraits>
class JsonSerialElementAccessor
{
    MP          memberPtr;
    public:
    JsonSerialElementAccessor(MP mp): memberPtr(mp)    {}
    void serialize(T const& src, std::ostream& stream) const
    {
        stream << jsonInternalExport(src.*memberPtr);
    }
    std::auto_ptr<ThorsAnvil::Json::SaxAction>      action(T& dst) const
    {
        std::auto_ptr<ThorsAnvil::Json::SaxAction>  action(new_JsonImportAction<typename SerTraits::SerializeInfo>(dst, memberPtr));
        return action;
    }
};

/* Helper functions */
template<typename T>
Importer<T, typename JsonSerializer::template Parser<T> >   jsonInternalImport(T& object)
{
    return Importer<T, typename JsonSerializer::template Parser<T> >(object);
}

template<typename T>
Exporter<T, typename JsonSerializer::template Printer<T> >  jsonInternalExport(T const& object)
{
    return Exporter<T, typename JsonSerializer::template Printer<T> >(object);
}

        }
    }
}


#endif


评论

我强烈怀疑Loki是否会以Thor命名。除非是为了某种邪恶的计划而讨好自己。

@KonradRudolph:注意:它的“雷神砧”。雷神用锤子打败他的东西(Mjölnir)。因此,在我的怪异模式中,Loki又有了另一个化名。 (不是Loki是Thor唯一的沮丧和克星,但他有点痛苦)。

更深一层。你赢了。

您的代码不再与GitHub存储库同步。考虑到您提出问题的年龄,我不确定您是否要对该版本进行审核或是您的新版本。但这可能是您查看自己的将近6年历史的代码的好机会。

@zeta。是的,存储库是根据其他帖子中的评论中收到的评论进行更新的。

#1 楼

在尝试复习令人难以置信的复杂代码之前,我先对您的{}-宗教,代码环绕和垂直间距进行评论。

最适合您,我和其他人的个人知识真是令人难以置信。其中一些注释似乎相互矛盾,但为了提高代码的可读性可能会做出一些牺牲。

以下是我所有的个人喜好

第一个{}-宗教,“ {}”应支持程序的结构,并通过直观地检查代码变得容易。

template<JsonSerializeType>
struct JsonSerializeBrace
{
    static char braces[];
};


'{'根本不提供更多信息,缩进已经告诉您下一行取决于前一行。

template<JsonSerializeType>
struct JsonSerializeBrace {
    static char braces[];
};


这提供了完全相同的视觉信息,即括号是结构的一部分。
这样做的另一个好处是可以避免某些垂直滚动。

通过快速的视觉检查,可以隐藏该块是否存在控件依赖项。

if (!item.first)
{   stream << ',';
}


如果您改为编写

if (!item.first) {   
     stream << ',';
}




if (!item.first)
    stream << ',';


后跟空白行,控制流程更加明显。


接下来,使用垂直间距,如在文本和纸张中一样,这里的间距使眼睛更容易并改善视觉搜索。

这段代码看起来像是一堵文本墙

template<typename T>
struct MemberPrinter<T, T>
{
    // A normal object just prints itself.
    void operator()(std::ostream& stream, T const& source)
    {
        stream << source;
    }
};
template<typename T>
struct MemberPrinter<T, void>
{
    void operator()(std::ostream& stream, T const& source)
    {}
};


,而不是在结构,函数或其他不同实体之间使用垂直间距。

template<typename T>
struct MemberPrinter<T, T> {
    // A normal object just prints itself.
    void operator()(std::ostream& stream, T const& source) {
        stream << source;
    }
};

template<typename T>
struct MemberPrinter<T, void> {
    void operator()(std::ostream& stream, T const& source) {
    }
};


现在我们可以轻松地看到存在2种不同的结构,从'{'移动中牺牲了1条保存的行。

 * THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess:  A generic accessor can be used to generate multiple items.
 *                                                      When de-serializing the Json can be applied to multiple elements.
 *                                                      Used manly for container classes like std::map


如果在':'后出现中断,则可能更容易理解:

 * THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess:  
 *     A generic accessor can be used to generate multiple items.
 *     When de-serializing the Json can be applied to multiple elements.
 *     Used manly for container classes like std::map



我时代码换行也是一个问题尝试阅读别人的代码,这是什么意思?这里实际上有2个不同的问题,一个是长线长度,第二个是这条长线实际做什么。

boost::mpl::for_each<typename JsonSerializeTraits<T>::SerializeInfo>(MPLForEachActivateItem<T, ThorsAnvil::Json::ScannerSax>(scanner, destination));


任何水平滚动都需要额外的时间,通常比垂直滚动要花费更多时间。由于它们的结合力较弱,因此在其中的'(),='处中断该行。代码后的“ //”注释可能也必须移动,通常是在代码之前插入新行,并使用与移动代码的行相同的缩进。

    boost::mpl::for_each<typename JsonSerializeTraits<T>::SerializeInfo>(
        MPLForEachActivateItem<T, ThorsAnvil::Json::ScannerSax>(
            scanner, destination
        ) // optionally combine the 2 ')'
    ); 


啊,实际上是2个函数调用,经过大量模板化,因此此代码
template<typename T,
         typename MemberToSerialize =
             typename JsonSerializeTraits<T>::SerializeInfo>
struct MemberScanner {
    void operator()(ThorsAnvil::Json::ScannerSax& scanner, T& destination) {
        boost::mpl::for_each<typename JsonSerializeTraits<T>::SerializeInfo>(
            MPLForEachActivateItem<T, ThorsAnvil::Json::ScannerSax>(
                scanner, destination
        ));
    }
};


在控件的最后一个','处有一个中断。模板参数,在eol之前,然后在模板参数中的'='。
现在我们可以看到

typename MemberToSerialize = 
             typename JsonSerializeTraits<T>::SerializeInfo>


“ for_each”中的模板参数,这是我以前无法做到的。如果这不仅仅用于SFINAE,则应使用'MemberToSerialize'。

template<typename T,
         typename MemberToSerialize =
             typename JsonSerializeTraits<T>::SerializeInfo>
struct MemberScanner {
    void operator()(ThorsAnvil::Json::ScannerSax& scanner, T& destination) {
        boost::mpl::for_each<MemberToSerialize>(
            MPLForEachActivateItem<T, ThorsAnvil::Json::ScannerSax>(
                scanner, destination
        ));
    }
};


如果这实际上是您的意思(而不仅仅是我误解了您的意图)这样做)使它更易于阅读。

这让我有一个烦恼,即长的名称空间+类型名,使用'using',typedef或别名可以在这里提供帮助。

template<typename T,
         typename MemberToSerialize =
             typename JsonSerializeTraits<T>::SerializeInfo>
struct MemberScanner {
    // only pollute this scope, alternatively use typedef/alias
    using ThorsAnvil::Json::ScannerSax;

    void operator()(ScannerSax& scanner, T& destination) {
        boost::mpl::for_each<MemberToSerialize>(
            MPLForEachActivateItem<T, ScannerSax>(scanner, destination)
        );
    }
};


2种用途确实偏低,但由于使用时间太长,因此可以考虑使用。

评论


\ $ \ begingroup \ $
不幸的是,我几乎不同意所有观点(除了使用use来缩短名称)。这更多的是样式审查,而不是代码审查。由于我的个人风格偏爱属于三个主要的风格类别之一(对可读性进行了一些调整),因此您的看法与我的评论不同。这样就没有太大用处,尤其是因为这些内容通常都包含在本地样式指南中。
\ $ \ endgroup \ $
–马丁·约克
2014年10月8日15:07



\ $ \ begingroup \ $
没有正确的风格和错误的风格。我认为您提到的缩进样式页面显示了这一点。
\ $ \ endgroup \ $
– Florian F
2014年10月8日15:51

\ $ \ begingroup \ $
@FlorianF,这就是为什么我提到它是我的偏爱的原因,因为样式不同,也是为什么我认为这很相关。
\ $ \ endgroup \ $
–Surt
2014年10月8日在16:07

\ $ \ begingroup \ $
公平地说:@Loki正在征求反馈,而Surt正在将其交给他。 Loki具有选择权:同意,不同意或从苏里的观点中学习。简单。
\ $ \ endgroup \ $
– Deian
16年7月21日在16:20

\ $ \ begingroup \ $
虽然在较大的评论范围内对样式进行一些评论可能会有所帮助,但仅给出在大多数情况下完全主观的格式提示实际上并不能提供任何实质性的反馈。这基本上等于说“您选择红色,但我更喜欢蓝色”。
\ $ \ endgroup \ $
– Harald Scheirich
17年9月1日在22:12