Why does params behave like this?
Asked Answered
D

4

24

Output

1

2

null

2

Code

class Program
{        
    static void Main(String[] args)
    {
        String s = null;
        PrintLength(s);
        PrintLength(s, s);
        PrintLength(null);
        PrintLength(null, null);    
        Console.ReadKey();
    }

    private static void PrintLength(params String[] items)
    {
        Console.WriteLine(items == null ? "null" : items.Length.ToString());
    }    
}
Detour answered 5/2, 2010 at 21:21 Comment(0)
H
30

This is a fairly frequently asked question. For details, see section 7.4.1 and 7.4.3.1 of the specification.

Briefly: a method with a params array is applicable in either its "normal form" or its "expanded form". That is, you can say

PrintLength(new string[] {"hello"}); // normal form
PrintLength("hello"); // expanded form, translated into normal form by compiler.

When given a call that is applicable in both forms, the compiler always chooses the normal form over the expanded form.

Suppose we chose the expanded form every time both were applicable. Suppose you had

void M(params object[] x) {}

How would you actually pass a null array to this thing if we always chose the expanded form? That would be impossible!

Suppose you said

M(new object[] { "hello" });

and we always chose the expanded form. What would this do? Well, an array of objects is an object, so this would choose the expanded form -- it would make another array, wrap this thing up in the array, and pass that!

The choice of expanded form over normal form leads to crazy results. Always choosing the normal form over the expanded form is the more sensible thing to do.

Hartzell answered 5/2, 2010 at 21:34 Comment(4)
Good post Eric. I might add that w/ the first PrintLength(null), you are passing a null value to the function, where as with the second example PrintLength(s) you are passing a string pointer (reference) which doesn't actually reference anything. However, the reference still exists in memory w/ a value of 0 since it is a null pointer.Sculpt
I was 90% sure this was the case but that 10% just kept nagging at me.Detour
Curious that the 'expanded form', is actually the 'contracted form' in colloquial termsDemur
This was bad language design IMHO. It would have been a lot better if C# required the params keyword on the method calls to disambiguate when using this less common "normal form" (similar to how ref and out keywords are used on calls). E.g.: M(params array); M(obj1, obj2); M(null); M(params null);. This would be similar to JS spread syntax, a much more consistent behavior. Oh well.Mithridate
F
5

It works as designed, I'd say:

PrintLength(s);

You're passing in a single string which is null - inside your method, items will not be null - it's an array of one element - of type string - of value null

PrintLength(s, s);

Same story here - you're passing in two elements, so items in your method will be an array of two strings - both of which are null themselves, but the array is not

PrintLength(null);

This obviously gets interpreted as a single NULL value and thus items is null. You're not passing in an array, nor an element of type string - you're just passing in a null value per se.

PrintLength(null, null);

That's again - an array of two elements, both of which are null - but the array per se is not null, since you're passing in two values.

It's a bit puzzling, maybe - but really: what you need to check for in your PrintLength method is not whether your Items as a whole is null - but whether the actual values items[0] and so forth are null.

What is a bit odd maybe - or counter-intuitive at first - is the fact the single explicit "null" value is treated as a "null" - rather than an array of a single item of value null. Why that is the case, and whether it could have been implemented differently - I don't know, quite honestly.

Futrell answered 5/2, 2010 at 21:29 Comment(0)
E
4

The PrintLength(null) is passing a null array where as PrintLength(null, null) is passing a string[] with a length of two containing two null string objects. It'd be the same as passing new string[] { null, null }

Hmm reading what I wrote maybe that doesn't actually answer your question shrug.

Edit:

This is probably why: You can send a comma-separated list of arguments of the type specified in the parameter declaration, or an array of arguments of the specified type.

http://msdn.microsoft.com/en-us/library/w5zay9db.aspx

Eros answered 5/2, 2010 at 21:26 Comment(2)
I think the question is, why does passing a single null not pass the equivalent of new string[] { null }? It seems like some sort of intentionally hardcoded behavior, but to what end is kind of mysterious.Adultery
Re: edit So it's interpreting the null as if you passed in string[] s = null; Hm. Interesting corner case.Adultery
G
0

As said in answer one:

  • 1: an array of strings with one element in it - the element is null

  • 2: an array of strings with two elements in it, both elements are null

  • null: null is passed into the method, not an array

  • 2: an array of nulls is passed into the method

Gremial answered 5/2, 2010 at 21:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.