【问题标题】:How can one get a property name as a string in managed c++如何在托管 C++ 中将属性名称作为字符串获取
【发布时间】:2012-05-09 09:44:44
【问题描述】:

我看到很多在 C# 中执行此操作的条目,但在 C++ 中没有看到任何条目

我在一些托管 C++ 代码中有一组属性,用于在 C# 部分和 c++ 部分之间传递数据。在 C# 方面,here 提供的答案效果很好,我想用 C++ 做类似的事情。链接中包含的解决方案副本:

string NameOf<T>(Expression<Func<T>> expr) {
    return ((MemberExpression) expr.Body).Member.Name;
}

var gmtList = new SelectList(repository.GetSystemTimeZones(),
    NameOf(() => tz.Id),
    NameOf(() => tz.DisplayName));

我的问题是我似乎无法获得正确的调用语法,特别是本节:

() => tz.DisplayName

我似乎无法在网上找到有关如何在 C++ 中执行此操作的资源,因此,如果有人有任何经验或链接,我将非常感谢任何帮助。

【问题讨论】:

    标签: visual-c++ properties c++-cli managed-c++


    【解决方案1】:

    我已经为所有这些 lambda 表达式和酷炫的 System::Linq 命名空间(包括 CLinq 库和其他东西)苦苦挣扎了几个小时,然后……我只是想:为什么不使用纯粹的静态决策呢?

    我们只是声明

    /// Convert X to "X", the classical preprocessing trick
    #define GetPropName(TheClassName, ThePropertyName) #ThePropertyName
    

    然后我们可以做

    Console::WriteLine( GetPropName(TimeZone, Id) );
    

    将“Id”打印在屏幕上。

    是的……现在是有趣的部分。类型安全。我听到有关此解决方案的评论风暴(“不!这不好,它不检查 ThePropertyName 是否在课堂上!”)

    好的。解决方案:让我们使用在虚拟 TheClassName 实例中使用 ThePropertyName 的宏生成一些无意义的代码。

    /// This macro will produce the compilation error if ThePropertyName is not in the class named TheClassName
    #define CheckForPropertyExistence(TheClassName, ThePropertyName) \
    /* Create an array of Objects which will be converted to string and ignored*/ \
    (gcnew array<System::Object^> { (gcnew TheClassName())->ThePropertyName })->ToString()
    
    /// We get the property name using the "dinosaur strategy" - good old macro concatenated with the empty string which in turn is formed in CheckFor() macro
    #define GetPropertyName(TheClassName, ThePropertyName) \
    (gcnew System::String(#ThePropertyName)) + CheckForPropertyExistence(TheClassName, ThePropertyName)->Substring(0,0)
    

    现在我们可以给出完整的示例:

    using namespace System;
    
    /// Sample class
    public ref class TheTimeZone
    {
    public:
        TheTimeZone()
        {
            _Id = 0;
            _DisplayName = "tmp";
        }
    
        property int Id
        {
        public:
            int get() {return _Id;}
            void set(int v) { _Id = v; }
        }
    
        property String^ DisplayName
        {
        public:
            String^ get() { return _DisplayName; }
            void set(String^ v) { _DisplayName = v; }
        }
    
    private:
        int _Id;
        String^ _DisplayName;
    };
    
    /// This macro will produce the error if ThePropertyName is not in the class named TheClassName
    #define CheckForPropertyExistence(TheClassName, ThePropertyName) \
    /* Create an array of Objects which will be converted to string and ignored*/ \
    (gcnew array<System::Object^> { (gcnew TheClassName())->ThePropertyName })->ToString()
    
    /// We get the property name using the "dinosaur strategy":
    /// good old macro concatenated with the empty string
    /// which in turn is formed in CheckFor() macro
    #define GetPropertyName(TheClassName, ThePropertyName) \
    (gcnew System::String(#ThePropertyName)) + \
    CheckForPropertyExistence(TheClassName, ThePropertyName)->Substring(0,0)
    
    /// To get properties from objects with no default constructor
    #define GetPropertyNameForObject(TheObject, ThePropertyName) \
    (gcnew System::String(#ThePropertyName)) + \
    (gcnew array<System::Object^> { (TheObject)-> ThePropertyName })->ToString()->Substring(0,0)
    
    /// Test for our macros
    int main(array<System::String ^> ^args)
    {
        /// Prints "Length"
        /// We cannot use default constructor here
        Console::WriteLine(GetPropertyNameForObject (gcnew System::String("test"), Length) );
    
        /// Prints "Id"
        Console::WriteLine(GetPropertyName (TheTimeZone, Id) );
    
    /// Uncomment and get the error
        //Console::WriteLine(GetPropertyName (TheTimeZone, Id23) );
    
        return 0;
    }
    

    【讨论】:

    • 相当漂亮/hacky...你不能用1 ? NULL : yourcode 装饰CheckForPropertyExistence,这样在运行时创建的对象就少了吗?
    • @xanatos:是的,这似乎是个好主意。三年过去了,我不记得创建对象的确切原因。也许只是“找到解决方案!”兴奋使我无法按照你的建议去做。
    【解决方案2】:

    C# 中的 Lambda 表达式是委托的语法糖,因此您必须找到与 Lambda 表达式等效的委托才能在 C++/CLI 中具有相同的功能。

    为了让您的生活更轻松,您可以探索为 Linq 提供 C++/CLI 包装器的 CLinq(请参阅“Lambda 表达式”部分)

    注意:不要将 C++11 lambda 表达式与 C# lambda 表达式混淆。前者仅支持本机代码。可以使用 C++11 lambda,但您需要做一些额外的工作来为它们提供委托(this CodeProject article 探索主题)

    【讨论】:

    • 没有与 C++/CLI 和托管运行时兼容的“委托等价物”。但是,有一些被忽略的宏!
    • @ViktorLatypov - 请阅读 C++/CLI 代表 here
    • 对不起,阿提拉。我的意思是原始问题中使用的具有“ X => F(X) ”语法的 Lambda。 System.Delegate 完全有效,但没有简单的方法可以反编译它实际引用的代码。
    • 确实,因此建议使用 CLinq 尝试允许与 C# lambdas 非常相似的东西
    • 我在 CLinq 中花了一个小时(当然,这很好),这是因为打算模仿原始解决方案中使用的 C# lambda。但后来我才意识到有一个基于宏的单行解决方案,它不依赖于我经常使用的 .net 功能。
    【解决方案3】:

    我意识到这是一个旧线程,但类似的东西也可能有效:

    String^ NameOfSanityCheck(Object^ object, String^ name) { return name; } // could parse with a regex string like: "(->)|(::)|(\.)"
    
    #define nameof(s) NameOfSanityCheck(s, #s)
    

    【讨论】:

      猜你喜欢
      • 2023-03-13
      • 2020-12-03
      • 1970-01-01
      • 2014-03-01
      • 2020-06-24
      • 1970-01-01
      • 1970-01-01
      • 2014-03-06
      • 2013-09-30
      相关资源
      最近更新 更多