Why can't I pass a property or indexer as a ref parameter when .NET reflector shows that it's done in the .NET Framework?

18.2k views Asked by At

Okay, I will cut and paste from .NET reflector to demonstrate what I'm trying to do:

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

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

    //More irrelevant code...
}

This line of code comes right out of System.Web.Security.SqlMembershipProvider.UpdateUser (System.Web.dll v2.0.50727) in the .NET Framework.

The SecUtility.CheckParameter requires a reference value as the first parameter, to which they're passing a property of the user passed in as the argument.

The definition of the CheckParameter code is:

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

Everything it's doing makes sense - on paper... so I knock up a quick little prototype for somewhere I'd like to use something similar:

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

This code won't compile - the error shows as:

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

Fair enough... but why won't my code allow me to do something that appears to be in the .NET Framework code base? Is this an error with the way .NET Reflector is interpreting the DLL or is this an error with the way I'm interpreting their code?

4

There are 4 answers

3
Darin Dimitrov On BEST ANSWER

I think it is some bad interpretation from Reflector. Actually if you write your code like this:

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

and compile it in Release mode you will see this in Reflector:

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

Remember that the C# compiler is not producing C# code but IL so what you see in Reflector is not always the reality. So to clearly understand what is going on under the hood you may look at the real code produced by the compiler:

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)

It is clear that a local variable is used.

1
Kyle Trauberman On

Try setting the value of the property to a variable before passing it to the function.

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

Its not the most elegant solution, but it should point you in the right direction. As Yuriy said in his comment above, its probably something to do with the fact that you aren't explicitly declaring a get and set for the property.

5
Jon Skeet On

It's a reflector bug. It isn't really passing the property by reference.

Here's some C# code which will reproduce it.

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 shows this code for Foo:

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

Not only is this invalid C#, but it's misleading - it would suggest that changes made to x within Bar would somehow modify p.Name - where that's not the case when you look at the original C# code.

In your original sample, it makes even less sense as UserName is a read-only property!

0
Tamilmaran On

When you pass a variable as a ref or out, the call actually points to the memory where it is originally located. So if you are allowed to pass the property as a reference, it means you are making the class member inconsistent. This is the reason behind this problem.