当.NET反射器显示它在.NET Framework中完成时,为什么我不能将属性或索引器作为ref参数传递?

Ben*_*ter 12 .net c# .net-reflector

好的,我将从.NET反射器剪切和粘贴以演示我正在尝试做的事情:

public override void UpdateUser(MembershipUser user)
{
    //A bunch of irrelevant code...

    SecUtility.CheckParameter(ref user.UserName, true, true, true, 0x100, "UserName");

    //More irrelevant code...
}
Run Code Online (Sandbox Code Playgroud)

这行代码来自.NET Framework中的System.Web.Security.SqlMembershipProvider.UpdateUser(System.Web.dll v2.0.50727).

SecUtility.CheckParameter需要一个引用值作为第一个参数,它们将传入的用户属性作为参数传递给它.

CheckParameter代码的定义是:

internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
{
    //Code omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

它所做的一切都很有意义 - 在纸面上...所以我在某个地方敲了一个快速的小原型,我想用类似的东西:

public class DummyClass
{
    public string ClassName{ get; set; }
}

public class Program
{
    private static DoSomething(ref string value)
    {
        //Do something with the value passed in
    }

    public static Main(string[] args)
    {
        DummyClass x = new DummyClass() { ClassName = "Hello World" };

        DoSomething(ref x.ClassName); //This line has a red squiggly underline 
                                      //under x.ClassName indicating the 
                                      //error provided below.
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码将无法编译 - 错误显示为:

"A property or indexer may not be passed as an out or ref parameter"
Run Code Online (Sandbox Code Playgroud)

很公平......但为什么我的代码不允许我做一些似乎在.NET Framework代码库中的东西?这是.NET Reflector解释DLL的方式的错误,或者这是我解释其代码的方式的错误?

Dar*_*rov 15

我认为这是Reflector的一些不好的解释.实际上,如果你编写这样的代码:

static void Main(string[] args)
{
    DummyClass x = new DummyClass();
    string username = x.ClassName;
    DoSomething(ref username);
}
Run Code Online (Sandbox Code Playgroud)

并在发布模式下编译它,您将在Reflector中看到:

static void Main(string[] args)
{
    DummyClass x = new DummyClass();
    DoSomething(ref x.ClassName);
}
Run Code Online (Sandbox Code Playgroud)

请记住,C#编译器不会生成C#代码,而是IL,因此您在Reflector中看到的并不总是现实.因此,为了清楚地了解底层的内容,您可以查看编译器生成的实际代码:

L_000f: callvirt instance string System.Web.Security.MembershipUser::get_UserName()
L_0014: stloc.0 
L_0015: ldloca.s str
L_0017: ldc.i4.1 
L_0018: ldc.i4.1 
L_0019: ldc.i4.1 
L_001a: ldc.i4 0x100
L_001f: ldstr "UserName"
L_0024: call void System.Web.Util.SecUtility::CheckParameter(string&, bool, bool, bool, int32, string)
Run Code Online (Sandbox Code Playgroud)

很明显,使用了局部变量.

  • Ben:是的,它在语义上是不同的.这就是为什么Darin说Reflector的解释不好.Reflector应该显示user.UserName被分配给本地,而本地被ref传递给CheckParameter.调试或释放模式不会影响这一点:Darin的源代码和"Reflectedored"代码显示的是Reflector反编译是错误的 - 它在语义上与他的源代码不同(并且非法,如您所观察到的). (2认同)

Jon*_*eet 6

这是一个反射器错误.它并没有真正通过引用传递属性.

这里有一些C#代码可以重现它.

using System;

class Person
{
    public string Name { get; set; }
}

class Test
{
    static void Main(){} // Just make it easier to compile

    static void Foo(Person p)
    {
        string tmp = p.Name;
        Bar(ref tmp);
    }

    static void Bar(ref string x)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

Reflector显示以下代码Foo:

private static void Foo(Person p)
{
    Bar(ref p.Name);
}
Run Code Online (Sandbox Code Playgroud)

这不仅是无效的C#,而且它具有误导性 - 它会暗示对x内部进行的更改Bar会以某种方式进行修改p.Name- 在您查看原始C#代码时并非如此.

在您的原始样本中,它UserName只是一个只读属性的意义!