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

StackOverflow https://stackoverflow.com/questions/1810785

  •  05-07-2019
  •  | 
  •  

好的,我将从.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的方式的错误,还是我解释代码的错误?

有帮助吗?

解决方案

我认为这是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)

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

其他提示

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

这里有一些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#,而且是误导性的 - 它会建议对 Bar 中的 x 所做的更改会以某种方式修改 p.Name - 当您查看原始C#代码时,情况并非如此。

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

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

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top