简体   繁体   English

为什么在C#和VB.NET之间获取成员表达式成员名称有所不同?

[英]Why does getting a member expression member name differ between C# and VB.NET?

I have the following C# method: 我有以下C#方法:

private static string GetMemberName<T>(Expression<Func<T>> expr)
{
    MemberExpression memberExpr = expr.Body as MemberExpression;

    if (memberExpr == null) 
        throw new ArgumentOutOfRangeException("Wrong type of lambda...");

    return memberExpr.Member.Name;
}

And I can use it like this to print the name of a class-level field, method param, or local var (note this is pre-C# 6.0 nameof operator ): 我可以像这样使用它来打印类级别字段,方法参数或本地var的名称(注意这是pre-C#6.0 nameof operator ):

private static int _myFieldVar  = 62;

private static void DoStuff(int myMethodParam)
{
    int myLocalVar = 2;
    Debug.Print(GetMemberName(() => myMethodParam)); // prints "myMethodParam"
    Debug.Print(GetMemberName(() => myLocalVar)); // prints "myLocalVar"
    Debug.Print(GetMemberName(() => _myFieldVar)); // _myFieldVariable
}

Now I want to convert this code to VB.NET, so here is the GetMemberName method: 现在我想将此代码转换为VB.NET,因此这里是GetMemberName方法:

Private Function GetMemberName(Of T)(expr As Expression(Of Func(Of T))) As String
    Dim memberExpr As MemberExpression = TryCast(expr.Body, MemberExpression)

    If memberExpr Is Nothing Then _
        Throw New ArgumentOutOfRangeException("Wrong type of lambda...")

    Return memberExpr.Member.Name
End Function

However, I'm noticing different results when I get the method param and local variable names, ie they are both prefixed with " $VB$Local_ ": 但是,当我得到方法参数和局部变量名称时,我注意到不同的结果,即它们都以“ $ VB $ Local_ ”为前缀:

Private _myFieldVar As Integer = 62

Private Sub DoThis(myMethodParam As Integer)
    Dim myLocalVar = 2
    Debug.Print(GetMemberName(Function() myMethodParam)) ' prints "$VB$Local_myMethodParam""
    Debug.Print(GetMemberName(Function() myLocalVar)) ' prints "$VB$Local_myLocalVar"
    Debug.Print(GetMemberName(Function() _myFieldVar)) ' prints "_myFieldVar()" 
End Sub

I googled "$VB$Local_" and found this post which is very similar. 我用Google搜索“$ VB $ Local_”,发现这个帖子非常相似。 However, I think my question is different because I'm not getting this behavior with properties. 但是,我认为我的问题不同,因为我没有使用属性获得此行为。 If I call this: 如果我这样称呼:

Debug.Print(GetMemberName(Function() MyProperty))

I get "MyProperty". 我得到“MyProperty”。 Moreover, my fundamental question is "why is the behavior different between C# and VB.NET, ie what is the meaning of "$VB$Local_" and why is it absent in C#" , whereas that post is more concerned with how to avoid that behavior in VB.NET. 此外,我的基本问题是“为什么C#和VB.NET之间的行为不同,即”$ VB $ Local_“的含义是什么?为什么它在C#中不存在” ,而那篇帖子更关心如何避免VB.NET中的那种行为。

As mentioned by Hans Passant the 2 compilers use slightly different naming strategies for handling local variables within a Linq expression tree. 正如Hans Passant所提到的,2个编译器使用稍微不同的命名策略来处理Linq表达式树中的局部变量。 Let's take a look at what both output's look like decompiled. 让我们来看看两个输出看起来像反编译。 I used ILSpy with all of the options unchecked in View => Options => decompiler tab. 我使用了ILSpy,在View => Options => decompiler选项卡中未选中所有选项。 I also simplified the output expression tree a bit to keep the answer concise. 我还简化了输出表达式树,以使答案简洁明了。

C# C#

    public static string Test()
    {
        int myLocalVar = 2;
        int myLocalVar2 = 2; // local varible never exported! note how it is not in the generated class
        myLocalVar2++;
        return GetMemberName(() => myLocalVar);
    }

output 产量

    [CompilerGenerated]
    private sealed class <>c__DisplayClass1_0
    {
        public int myLocalVar;
    }

    public static string Test()
    {
        Class1.<>c__DisplayClass1_0 <>c__DisplayClass1_ = new Class1.<>c__DisplayClass1_0();
        <>c__DisplayClass1_.myLocalVar = 2;
        int num = 2;
        num++;
        return Class1.GetMemberName<int>(
            Expression.Lambda<Func<int>>(
                Expression.MakeMemberAccess(
                    Expression.Constant(<>c__DisplayClass1_, typeof(Class1.<>c__DisplayClass1_0)),
                    typeof(Class1.<>c__DisplayClass1_0).GetMember("myLocalVar")
                )
            )
        );
    }

VB VB

Public Shared Function Test() As String
    Dim myLocalVar As Integer = 2
    Dim myLocalVar2 As Integer = 2
    myLocalVar2 = myLocalVar2 + 1
    Return GetMemberName(Function() myLocalVar)
End Function

output 产量

    [CompilerGenerated]
    internal sealed class _Closure$__2-0
    {
        public int $VB$Local_myLocalVar;
    }

    public static string Test()
    {
        Class1._Closure$__2-0 closure$__2- = new Class1._Closure$__2-0();
        closure$__2-.$VB$Local_myLocalVar = 2;
        int num = 2;
        num++;
        return Class1.GetMemberName<int>(
            Expression.Lambda<Func<int>>(
                Expression.MakeMemberAccess(
                    Expression.Constant(closure$__2-, typeof(Class1._Closure$__2-0)), 
                    typeof(Class1._Closure$__2-0).GetMember("$VB$Local_myLocalVar")
                )
            )
        );
    }

Both compilers create a private sealed class for myLocalVar . 两个编译器都为myLocalVar创建一个private sealed class This is done to satisfy the requirements of the Linq expression tree. 这样做是为了满足Linq表达式树的要求。 The expression tree needs to capture a reference to the local variable. 表达式树需要捕获对局部变量的引用。 The below example shows why this is needed. 以下示例说明了为什么需要这样做。

       int localVar = 1;
       Expression<Func<int>> express = () => localVar;
       var compiledExpression = express.Compile();
       Console.WriteLine(compiledExpression());//1
       localVar++;
       Console.WriteLine(compiledExpression());//2
       Console.ReadLine();

Back to the question- why is the behavior different between C# and VB.NET, ie what is the meaning of "$VB$Local_" and why is it absent in C#? 回到问题 - 为什么C#和VB.NET之间的行为不同,即“$ VB $ Local_”的含义是什么?为什么它在C#中不存在?

The compilers generate an unbelievable amount of code for us, C# and VB.NET both do it slightly diffrently. 编译器为我们生成了令人难以置信的代码量,C#和VB.NET都略微不同。 So I am only going to answer why does VB insert $VB$Local_ . 所以我只想回答为什么VB插入$VB$Local_ ** To avoid name collisions.** Both C#'s DisplayClass and VB.Net's Closure is used for more than one purpose. **避免名称冲突。** C#的DisplayClass和VB.Net的Closure都用于多个目的。 To keep from having a collision, the name is prefixed with a key that represents the source. 为防止发生冲突,名称前缀为代表源的键。 It just works out that C#'s key is nothing, all other language features that contribute to DisplayClass prefix with something else. 事实证明,C#的关键是什么,所有其他语言功能都有助于DisplayClass前缀与其他东西。 Try decompiling the folowing VB.net to see why the prefix key is needed. 尝试反编译下面的VB.net,看看为什么需要前缀密钥。

Sub Main()
    Dim myLocalVar As Integer = 2
    Dim x1 As System.Action(Of Integer) = Sub(x)
                                              System.Console.WriteLine(x)
                                              GetMemberName(Function() myLocalVar)

                                          End Sub
    x1(2)
End Sub

The compiled closure will be the following. 编译后的闭包将如下。

    [CompilerGenerated]
    internal sealed class _Closure$__2-0
    {
        public int $VB$Local_myLocalVar;

        internal void _Lambda$__0(int x)
        {
            Console.WriteLine(x);
            Class1.GetMemberName<int>(Expression.Lambda<Func<int>>(Expression.Field(Expression.Constant(this, typeof(Class1._Closure$__2-0)), fieldof(Class1._Closure$__2-0.$VB$Local_myLocalVar)), new ParameterExpression[0]));
        }

async and await also use closures in this way, however they generate a lot of boilerplate code that I didn't want to paste in here. async和await也以这种方式使用闭包,但是它们生成了很多样板代码,我不想在这里粘贴。


Final remarks 最后的评论

Your passing in a local variable and parameter into a method named GetMemberName . 您将局部变量和参数传递到名为GetMemberName的方法中。 It was only luck that under the hood the 2 compilers automatically generated types and members to satisfy linq expression trees in the first place. 只有幸运的是,2个编译器首先自动生成类型和成员以满足linq表达式树。 Fortunately the latest iteration of the compilers have the nameof operator, which is a much better way to handle this problem. 幸运的是,编译器的最新版本具有nameof运算符,这是处理此问题的更好方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM