Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ensure compilation error on signature change where 'params' keyword is used

I have a method like this:

public void Foo(params string[] args) {
  bar(args[0]); 
  bar(args[1]);
}

The new requirements lead to a change like this:

public void Foo(string baz, params string[] args) {
  if("do bar".Equals(baz)) {
    bar(args[0]); 
    bar(args[1]);
  }
}

The problem is that even though I've changed the method signature, no compilation errors occur, which is correct of course, but I want there to be compilation errors for every call to Foo method where the argument baz has not been specified. That is, if a call to Foo before the change was this one:

Foo(p1,p2); //where p1 and p2 are strings

it now needs to be this one:

Foo(baz,p1,p2);

If it wouldn't be changed in this way, p1 would be assigned to baz, and the params array args would be of length 1 and an OutOfBounds exception would be thrown.

What's the best way to change the signature and ensure that all the calling code is updated accordingly? (The real scenario is where Foo lives in an assembly shared by many projects automatically built on a build server. A compilation error would thus be an easy way to detect all the code that needs to be touched to accomodate the change.)

Edit: As Daniel Mann and others pointed out, the example above suggests that I should not use params at all. So I should explain that in my real world example it's not always the case that args needs to have two elements, as far as the logic in Foo is concerned args can contain any number of elements. So let's say this is Foo:

public void Foo(string baz, params string[] args) {
  if("do bar".Equals(baz)) {
    int x = GetANumberDynamically();
    for(int i = 0; i<x; i++)
      bar(args[i]); 
  }
}
like image 698
Manolo Avatar asked Dec 05 '13 13:12

Manolo


People also ask

What does the params keyword do when used in a method parameter list?

By using the params keyword, you can specify a method parameter that takes a variable number of arguments. The parameter type must be a single-dimensional array. No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration.

Why do we use params in C#?

Params is an important keyword in C#. It is used as a parameter which can take the variable number of arguments. Important Point About Params Keyword : It is useful when programmer don't have any prior knowledge about the number of parameters to be used.


2 Answers

Here's the solution. Do not change the former method signature, just add the Obsolete attribute with both arguments specified.

[Obsolete("Use Foo(string, params string[]) version instead of this", true)]
public void Foo(params string[] args) {
  bar(args[0]); 
  bar(args[1]);
}

Then create a new method with a new signature.

public void Foo(string baz, params string[] args) {
  if("do bar".Equals(baz)) {
    bar(args[0]); 
    bar(args[1]);
  }
}

The second argument in the Obsolete attribute ensures a compilation error. Without it it just causes a compilation warning. More info about the attribute is available on MSDN.

EDIT:

Based on discussion in comments below, Daniel Mann came up with an interesting problem.

That wouldn't solve the problem. What about if you call Foo("a", "b")? In that case, it will still call the non-obsolete method with only two arguments, and cause the same problem.

I would advise to check if there is more then one argument passed through args before calling bar.

like image 188
Ondrej Janacek Avatar answered Sep 21 '22 21:09

Ondrej Janacek


The easiest solution is to not use the params keyword if you have required parameters.

Obviously, you're expecting args to contain at least two parameters. It's safe to say that those are required. Why not have a method signature like this?

public void Foo(string baz, string requiredArgument1, string requiredArgument2, params string[] optionalArguments)

That removes the ambiguity: It will always require at least 3 arguments.

Another option I hadn't even thought of for some reason is to use named parameters. Obviously, all of your code would have to explicitly do so, but you could do this:

Foo(baz: "bar", args: new [] {"a", "b", "c"});

like image 35
Daniel Mann Avatar answered Sep 20 '22 21:09

Daniel Mann