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?
Asked Answered
K

4

13

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?

Karelian answered 27/11, 2009 at 22:8 Comment(5)
You are using an automatic property. When it gets compiled, it is turned into two functions. So the framework can't pass a reference to two functions into something else.Vicky
@Yuriy - that would be understandable. However, in the code that caused me to notice this issue, I'm using the same MembershipUser class referenced in the Framework in exactly the same way - how come therefore it can be done inside the framework but not outside?Karelian
Where's @JonSkeet when you need him...Karelian
@Ben: I'm coming, I'm coming... give me a couple of minutes more :)Hexosan
Looks like it is reflector issue. In IL code local variable is created onto the stack and passed to CheckParameter() methodDesuetude
D
17

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.

Drank answered 27/11, 2009 at 22:21 Comment(3)
@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"...Karelian
@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?Karelian
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).Retiring
H
6

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!

Hexosan answered 27/11, 2009 at 22:30 Comment(5)
My original sample is cut and pasted right from reflector, which is what got me scratching my headKarelian
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 :PKarelian
@BenAlabaster: My "Reflector shows this code..." bit was cut and pasted right from reflector too :)Hexosan
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?Karelian
@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.Hexosan
I
1

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.

Imbecility answered 27/11, 2009 at 22:23 Comment(1)
@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...Karelian
R
0

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.

Rework answered 13/2, 2012 at 13:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.