6

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

我在一些托管 C++ 代码中有一组属性,用于在 C# 部分和 c++ 部分之间传递数据。在 C# 方面,这里提供的答案非常有效,我想用 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++ 中执行此操作的资源,因此,如果有人有任何经验或链接,我将非常感谢任何帮助。

4

3 回答 3

6

我已经为所有这些 lambdas 和很酷的 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;
}
于 2012-05-21T19:09:41.557 回答
2

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

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

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

于 2012-05-16T11:12:03.187 回答
1

我意识到这是一个旧线程,但这样的事情也可能有效:

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

#define nameof(s) NameOfSanityCheck(s, #s)
于 2020-11-11T19:17:56.637 回答