mkelley33
mkelley33

Reputation: 5601

C# variable length args, which is better and why: __arglist, params array or Dictionary<T,K>?

I recently read the following overflow post: Hidden Features of C#

One of the features pointed out was the arglist. Why would one choose this or the alternatives as a means of using a variable length argument list to a method? Also, note that I would probably not use this kind of construct in my code unless a corner case warranted doing so. This is more of a question of semantics than whether it is even practical or prudent to even use variable length arguments. So does anyone know which is better and why?

 [Test]
 public void CanHandleVariableLengthArgs()
 {
     TakeVariableLengthArgs(__arglist(new StringBuilder(), 12));

     object[] arr = { new StringBuilder() };
     TakeVariableLengthArgs2(arr);

     TakeVariableLengthArgs3(
         new Dictionary<string, object> 
         { { "key", new StringBuilder() } });
 }

 public void TakeVariableLengthArgs(__arglist)
 {
      var args = new ArgIterator(__arglist);

      var a = (StringBuilder)TypedReference.ToObject(args.GetNextArg());
      a.Append(1);
 }

 public void TakeVariableLengthArgs2(params object[] args)
 {
      var a = (StringBuilder)args[0];
      a.Append(1);
 }

 public void TakeVariableLengthArgs3(Dictionary<string, object> args)
 {
      var a = (StringBuilder)args["StringBuilder"];
      a.Append(1);
 }

Upvotes: 12

Views: 16631

Answers (5)

bzlm
bzlm

Reputation: 9727

C# 4 will have a better mechanism for this; named and optional arguments:

static void Main(string[] args)
{
    // The method can be called in the normal way, by using positional arguments.
    Console.WriteLine(CalculateBMI(123, 64));

    // Named arguments can be supplied for the parameters in either order.
    Console.WriteLine(CalculateBMI(weight: 123, height: 64));
    Console.WriteLine(CalculateBMI(height: 64, weight: 123));

    // Positional arguments cannot follow named arguments.
    // The following statement causes a compiler error.
    //Console.WriteLine(CalculateBMI(weight: 123, 64));

    // Named arguments can follow positional arguments.
    Console.WriteLine(CalculateBMI(123, height: 64));
}

static int CalculateBMI(int weight, int height)
{
    return (weight * 703) / (height * height);
}

Upvotes: 3

LBushkin
LBushkin

Reputation: 131796

In general, you are probably better off avoiding undocumented features of the language - for several reasons.

  • They are more likely to change then the established, documented features
  • They may have side-effects and implications that are not obvious in their usage
  • Other developers will not be familiar with them and will have a harder time maintaining your code
  • Refactoring tools (like VS itself or Resharper) are unlikely to be able to recognize them
  • They take away clarity from the intent of your code
  • There are language-supported alternatives to most of them

In the specific case of the __arglist, you can achieve the same capabilities with the language supported params keyword, which allows you to create type-safe variable argument lists for methods in C#. As a practice, though, I would be careful using it as it can obfuscate your code if used inpropertly - good use cases (like those in string.Format() which accepts variable arguments) - are less frequent than you would think.

Upvotes: 3

plinth
plinth

Reputation: 49199

It depends on the case. I've used params in cases where I have a variable number of arguments and it significantly adds to the readability of the calling code.

For example, I have a class that represents a TIFF document and allows access to a collection of pages which can be reordered and interspersed with other TIFF documents. Since one of the most common tasks our customers want is the ability to easily combine multiple TIFF documents into a single, we also provide the following two utility methods:

public static void Combine(Stream output, params Stream[] sources) { /* ... */ }
public static void Combine(Stream output, params string[] sourceFiles) { /* ... */ }

which in usage make the client code feel really nice:

using (FileStream output = new FileStream(outputPath, FileMode.Create)) {
    TiffDocument.Combine(output, tpsCoverSheetPath, mainDocumentPath, tpsTrailerPath);
}

Upvotes: 3

krosenvold
krosenvold

Reputation: 77201

I would prefer to not use any of the three techniques described here. I would instead design a value object that has strong types wherever possible, and possibly even nullable types. If push comes to shove you can create a generics-typed value object too.

There's just all so much code smell in this way of coding for me. A variable length collection of object ? Wouldn't pass unnoticed in my code review.

Edit: And if it DID pass my code review the parameter would in all likelyhood be an IEnumerable instance and none of the three suggestions items. IEnumerable is the leanest thing that could encapsulate my needs.

Upvotes: 1

John Saunders
John Saunders

Reputation: 161821

I would certainly never use __arglist, since it's undocumented and nobody knows what it means in any case.

I'd also avoid variable-length argument lists for as long as possible, and instead rework my design to understand what is truly variable, and to model that variability in a less platform-dependant manner.

Upvotes: 12

Related Questions