views:

123

answers:

4

The idea behind this code is that it uses an extension method to instantiate an instance of a class if that class is set to null. The code is simple enough, but does not work (it compiles and runs, but at the end the object reference is still null).

Can anyone suggest why?

The following code uses a simple class SomeClass that has a single string property.

class SomeClass
{
    public string SomeProperty { get; set; }
}


static class ExtensionMethods
{
    public static void InitialiseObjectIfNull<T>(this T obj)
        where T : class, new()
    {
        if (obj == null) obj = new T();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SomeClass someClass = null;

        someClass.InitialiseObjectIfNull();

        // someClass is still null - but why??
    }
}

(The discussion about whether or not this is an appropriate use of an extension method should be considered outside the scope of the question! I am interested in understanding why this approach does not work)

Edit

On closer inspection this question is less about extension methods, and more about what is going on when you pass a reference type with or without the ref keyword.

The following function will cause a passed obj to be initialised for the caller:

static void InitialiseObjectIfNull<T>(ref T obj) where T : class, new()
{
    if (obj == null) obj = new T();
}

InitialiseObjectIfNull<SomeClass>(ref someClass); // someClass will be initialised

The following function will not cause a passed obj to be initlaised for the caller:

static void InitialiseObjectIfNull<T>(T obj) where T : class, new()
{
    if (obj == null) obj = new T();
}

InitialiseObjectIfNull<SomeClass>(someClass); // someClass will not be initialised

But...we're dealing with a reference type here, so what does the CLR do with the newd obj if the ref keyword is not used? Presumably it just gets garbage collected...

Edit2

OK, we're going back to basics here. Consider the code below:

class Program
{
    static void SetProperty(SomeClass someClass)
    {
        someClass.SomeProperty = "Bar";
    }

    static void Main(string[] args)
    {
        SomeClass someClass = new SomeClass { SomeProperty = "Foo" };

        SetProperty(someClass);

        // someClass.SomeProperty now equals "Bar"
    }
}

Note: someClass was not passed using the ref keyword, but the value of its property was still changed for the caller. This is what I would expect to see.

However, change the SetProperty function as follows:

static void SetProperty(SomeClass someClass)
{
    someClass = new SomeClass { SomeProperty = "Bar" };
}

...and the caller will not see any change to someClass.

+5  A: 

You would need to return the value; extension methods aren't ref on the this argument:

static class ExtensionMethods
{
    public static T InitialiseObjectIfNull<T>(this T obj)
        where T : class, new()
    {
        return obj ?? new T();            
    }
}

Then you'd need:

someClass = someClass.InitialiseObjectIfNull();

Personally, I expect it would be simpler to do it directly... with either:

if(someClass == null) someClass = new SomeClass();

or

someClass = someClass ?? new SomeClass();
Marc Gravell
Then my calling code would have to be someClass = someClass.InitialiseObjectIfNull(), which is kind of mangled.
Richard Ev
Yes. That is entirely correct. See update.
Marc Gravell
+3  A: 

this T obj would need to be a ref argument. Since that's not allowed (MSDN or C# In Depth p258, Skeet), you can't.

I would suggest you not push this idea too far, though. It seems like a confusing application of extension methods, especially when the alternative is still a short one line.

Michael Haren
While Jon Skeet is *almost* the same as the spec, I suspect "10.6.9 in the C# Language Specification Version 3.0" would be a better reference.
Marc Gravell
Agreed, it's not a good use of an extension method. I was curious however to know why it didn't work
Richard Ev
@Marc: that's true. In my case, though, I have Skeet's book at hand, not the spec. I added a link to a more accessible blog on MSDN which might be more appropriate.
Michael Haren
+4  A: 

Extension method is just syntactic sugar to this:

ExtensionMethods.InitialiseObjectIfNull(someClass);

You're passing someClass by value, thus assigning to it inside InitialiseObjectIfNull body will have no effect unless you pass someClass by reference, which you can't do in case of extension methods: you cannot pass this in this T obj by reference.

Anton Gogolev
+1: it makes extension methods much clearer to think of them this way
Michael Haren
+2  A: 

This doesn't work because the obj parameter is not declared passed by reference. Unfortunately, you can't declare a parameter as "this ref T obj", it's not supported... I know, I tried that yesterday ;)

You could do something like that instead :

static class ExtensionMethods
{
    public static T InitialiseObjectIfNull<T>(this T obj)
        where T : class, new()
    {
        return obj ?? new ();
    }
}

class SomeClass
{
    public string SomeProperty { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        SomeClass someClass = null;

        someClass = someClass.InitialiseObjectIfNull();
    }
}
Thomas Levesque