1

I'm new to C# scripting.

I'm reading some code , and inside a class method there is a call to a function to add to a Dictionary instance named Listeners like this:

public class ......
{
    private Dictionary<string, List<Component>> Listeners = 
        new Dictionary<string, List<Component>>();

    public void AddListener(Component Listener, string NotificationName)
    {
        // Add listener to dictionary
        if (!Listeners.ContainsKey(NotificationName))
            Listeners.Add (NotificationName, new List<Component>());

        // Add object to listener list for this notification
        Listeners[NotificationName].Add(Listener);
    }
}

Now, it seems the call to Listeners.Add has a constructor call

new List<Component>()

as an argument.

Am I getting it right ?

vgru
  • 49,838
  • 16
  • 120
  • 201
  • 1
    Couple things that will make your life easier. First, there is a comma missing on the method declaration, right? Second, name your local variables, formal parameters and local variables using the `camelCaseConvention`, not the `PascalCaseConvention`. It reads very strangely to experienced C# programmers to see a formal named `NotificationName`. It should be `notificationName`. The compiler does not care, but the reader of your code will have the wrong intuitions about your code if you don't follow standard naming conventions. – Eric Lippert Oct 25 '15 at 14:56
  • You never have a *call* as an argument. You either have the return value of the call or a function pointer. – Thomas Weller Oct 25 '15 at 15:03
  • 1
    @ThomasWeller: I am confused by your comment. Plainly the *argument* is a call -- or rather, an object creation; the value of the *formal parameter* corresponding to the argument will be the value produced by *evaluating the object creation*. It seems bizarre to say "the argument isn't a call, it is the result of evaluating a call". Would you say the same thing about, say, an integer literal? The argument to `abc(123)` is not an integer literal, it is the value produced by evaluating the integer literal? This is a hairsplitting distinction; the value of all expressions is their evaluation! – Eric Lippert Oct 25 '15 at 15:07
  • @EricLippert: You can pass objects as arguments. And a call is a MSIL `call` instruction. You can't pass that as an argument. An integer is an object, so it can be passed as argument. – Thomas Weller Oct 25 '15 at 15:12
  • 1
    @ThomasWeller: Nowhere in the C# specification does it say that a call is a `call` instruction. It says that a call is an *expression* whose *value* is the result of the call, and it says that *arguments* are *expressions*, and it says that arguments *correspond to formal parameters*, and it says that the values of the expressions will be copied to the formal parameters. Arguments are expressions. Calls are expressions. Therefore an argument can be a call. This seems very straightforward to me. – Eric Lippert Oct 25 '15 at 15:17
  • duplication: https://stackoverflow.com/questions/4245064/method-to-add-new-or-update-existing-item-in-dictionary – kain64b Oct 25 '15 at 15:20
  • 1
    @kain64b: This is not a duplicate of that question. That question is specifically about dictionary code. This question is about the general pattern of using a complex expression as an argument; that it happens to be about dictionaries is just a coincidence. – Eric Lippert Oct 25 '15 at 15:26

2 Answers2

4

Now , it seems the call to Listeners.Add ...

You mean not the call to Listeners[NotificationName].Add I assume.

... has a constructor call new List<Component>() as an argument. Am I getting it right ?

Yes, the call has two arguments. The first argument is a formal parameter of the current method, and the second argument is an object creation expression. The values of those arguments at runtime will be a reference to a string and a reference to a newly created object.

The order of operations here is, first the receiver is evaluated, then those argument values will be generated, and then the Add method will be invoked with those values copied into its formal parameters and the receiver value used as this.

The meaning of the code is "I have a map from names to lists of stuff; if this map has no mapping for a particular name, create a mapping from the name to an empty list of stuff". The second Add call then adds a single element to that list of stuff.

From a comment on another answer:

I cannot get my hands on the instance reference outside that function call

Well, you do get your hands on it, on the very next line, by reading it right back out of the map again. But without a device that allows you to obtain that value by evaluating another expression, as you do here, you are correct. That expression simply produces a value; it does not associate any name with it that you can then use later on. If you'd wanted to do that then you'd want to create a local variable.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
0

Nope, the call gets a new instance of a List<Component> as argument.

Unlike methods, constructors cannot be really referenced. While you can store a method reference in a delegate and later call it, you cannot do the same with constructors. Although that is more or less moot here, because

Listeners.Add (NotificationName, new List<Component>());

means roughly the following:

var param1 = NotificationName;
var param2 = new List<Component>();
Listeners.Add(param1, param2);

As you can see, a new list is created, and that new list is passed to the Add method.

Joey
  • 344,408
  • 85
  • 689
  • 683
  • Well, new List() is a constructor call right ? Then this call generates an instance which is passed as the function argument. But in the first case I cannot get my hands on the instance reference outside that function call. – GratuitousObfuscation Oct 25 '15 at 14:49