好的,我将从.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的方式的错误,还是我解释代码的错误?
答案 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传递时,该调用实际上指向它最初所在的内存。因此,如果允许您将属性作为引用传递,则意味着您使类成员不一致。这就是这个问题背后的原因。