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

时间:2009-11-27 22:08:59

标签: c# .net .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...
}

这行代码来自.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
}

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

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.
    }
}

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

"A property or indexer may not be passed as an out or ref parameter"

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

4 个答案:

答案 0 :(得分:15)

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

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

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

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

请记住,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)

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

答案 1 :(得分: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)
    {
    }
}

Reflector显示Foo的代码:

private static void Foo(Person p)
{
    Bar(ref p.Name);
}

这不仅是无效的C#,但它具有误导性 - 它会建议x内的Bar所做的更改会以某种方式修改p.Name - 当你看到时不是这种情况原始的C#代码。

在原始示例中,由于UserName是只读属性,因此更不合理!

答案 2 :(得分:1)

尝试将属性的值设置为变量,然后再将其传递给函数。

string myClassName = x.ClassName
DoSomething(ref myClassName);

它不是最优雅的解决方案,但它应该指向正确的方向。正如Yuriy在上面的评论中所说,它可能与你没有明确宣布物业的获取和设置这一事实有关。

答案 3 :(得分:0)

当您将变量作为ref或out传递时,该调用实际上指向它最初所在的内存。因此,如果允许您将属性作为引用传递,则意味着您使类成员不一致。这就是这个问题背后的原因。