views:

687

answers:

4

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?

+10  A: 

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.

Darin Dimitrov
@Darin - but isn't that semantically different? Isn't username now a copy of x.ClassName and we're passing a reference to the string "username" not a reference to the string "x.ClassName"...
BenAlabaster
@Darin - that release mode code extract now has me scratching my head... how does it change the code to something I can't code myself because the compiler doesn't allow it?
BenAlabaster
Same thing in Debug mode :-)
Darin Dimitrov
Ben: yes, it is semantically different. This is why Darin says it is bad interpretation from Reflector. Reflector should be showing user.UserName being assigned to a local, and the local being passed by ref to CheckParameter. Debug or Release mode doesn't affect this: what Darin's source and "Reflectored" code are showing is that the Reflector decompilation is wrong -- it is semantically different from his source code (and illegal as you have observed).
itowlson
+1  A: 

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.

Kyle Trauberman
@Kyle - I'd already written this off as being semantically different. Because I was after a reference to x.ClassName. myClassName would now be a copy of the string and I'd be passing a reference to the copy. But now @Darin Dimitrov has me wondering about release mode compilation...
BenAlabaster
+4  A: 

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!

Jon Skeet
Thanks for your input :)
BenAlabaster
My original sample is cut and pasted right from reflector, which is what got me scratching my head
BenAlabaster
when I say "from reflector" I mean of course ".NET Reflector's interpretation of System.Web.dll (v2.0.50727)" - I know you're a stickler for accurate terminology :P
BenAlabaster
@BenAlabaster: My "Reflector shows this code..." bit was cut and pasted right from reflector too :)
Jon Skeet
Further question for you then: I don't know how Red Gate's Reflector works under the covers nor am I great with IL. Is Red Gates' reflector built upon .NET reflection and hence is this a limitation with .NET Reflection or is this a problem that only affects the Reflector tool?
BenAlabaster
@Ben: The reflection APIs don't do decompilation - that's all down to Reflector. I don't know whether Reflector just uses reflection or (more likely IMO) reads the files and analyses them directly.
Jon Skeet
A: 

I would suggest trying to code this in vb.net as it has never suffered from this problem in my experience. This problem has always been one of my annoyances when I work with c#.

swight