11

Is there some kind of equivalent of Python's **kwargs in C#? I would like to be able to pass variable number of named arguments into functon, then get them as something Dictionary-like inside function and cycle over them.

Shadow
  • 8,749
  • 4
  • 47
  • 57
src091
  • 2,807
  • 7
  • 44
  • 74
  • 3
    I was initially thinking about the params keyword, but then I noticed you said "variable number of named arguments". The only way I can think of is to create a Dictionary and pass that in. – Sam Dec 18 '12 at 14:05
  • After considering all the options I've decided to take your advice. – src091 Dec 18 '12 at 14:32

6 Answers6

15

There is nothing in C# available to let you pass in arbitrary named parameters like this.

You can get close by adding a Dictionary<string, object> parameter, which lets you do something similar but requiring a constructor, the "parameter names" to be strings and some extra braces:

static void Method(int normalParam, Dictionary<string, object> kwargs = null)
{
   ...
}

Method(5, new Dictionary<String, object>{{ "One", 1 }, { "Two", 2 }});

You can get closer by using the ObjectToDictionaryRegistry here, which lets you pass in an anonymous object which doesn't require you to name a dictionary type, pass the parameter names as strings or add quite so many braces:

static void Method(int normalParam, object kwargs = null)
{
    Dictionary<string, object> args = ObjectToDictionaryRegistry(kwargs);
    ...
}

Method(5, new { One = 1, Two = 2 });

However, this involves dynamic code generation so will cost you in terms of performance.

In terms of syntax, I doubt you'll ever be able to get rid of the `new { ... }' wrapper this requires.

Rawling
  • 49,248
  • 7
  • 89
  • 127
  • Thanks for capturing the essence of my question with me forgetting to outline it :) – src091 Dec 18 '12 at 14:30
  • http://blog.andreloker.de/post/2008/05/03/Anonymous-type-to-dictionary-using-DynamicMethod.aspx (linked at this: "the ObjectToDictionaryRegistry here") is not found :\ – 0x01010 Aug 29 '23 at 16:43
3

If you specifically want a series of KeyValuePairs instead of an array of values, you could do something like this:

public class Foo
{
    public Foo(params KeyValuePair<object, object>[] myArgs)
    {
        var argsDict = myArgs.ToDictionary(k=>k.Key, v=>v.Value);
        // do something with argsDict
    }
}

myArgs would be an array of KeyValuePair<object, object> that you can iterate or convert to a dictionary as shown above. Just a note though, the conversion to dictionary might fail if you pass multiple KeyValuePair<>s with the same key. You might have to do some checking ahead of time before converting to a dictionary.

You would call it like this:

KeyValuePair<object, object> myKvp1 = new KeyValuePair<object, object>(someKey1, someValue1);
KeyValuePair<object, object> myKvp2 = new KeyValuePair<object, object>(someKey2, someValue2);
KeyValuePair<object, object> myKvp3 = new KeyValuePair<object, object>(someKey3, someValue3);

Foo foo = new Foo(myKvp1, myKvp2, myKvp3);
psubsee2003
  • 8,563
  • 8
  • 61
  • 79
  • Thanks, it gets the job done but, well I hoped I could reduce the amount of code by calling `fun(a:1, b:2)` instead of `fun(new Dictionary{ {"a", 1}, {"b", 2} })`. In this regard passing a dictionary is shorter then your approach. – src091 Dec 18 '12 at 14:25
  • @Anton passing a dictionary directly would be faster (and clearer) yes. My example was more about using the KeyValuePairs to pass semi named arguments. The conversion to dictionary was more about illustrated what you could do with the data once inside the constructor. – psubsee2003 Dec 18 '12 at 14:28
1

Yes. The ability to use optional and named parameters was added in, I believe, C# 4.0.

http://msdn.microsoft.com/en-us/library/dd264739(v=vs.100).aspx

ThatBlairGuy
  • 2,426
  • 1
  • 19
  • 33
  • 3
    Does not satisfy "arbitrary number of" part. – Deestan Dec 18 '12 at 14:14
  • Edited. If you go to that link, optional parameters were added at the same time. – ThatBlairGuy Dec 18 '12 at 14:18
  • Yes, but you can still not use that to make a function accepting an "arbitrary number of named parameters". As is, you are restricted by which optional parameters are defined in the receiving function's type signature. This clarifies things a bit, I think: http://stackoverflow.com/questions/1769403/understanding-kwargs-in-python – Deestan Dec 18 '12 at 14:23
1

Using of tuples is another way to solve this problem.

Here is the example code:

//  if you want, replace dynamic type with all object types
static void CSharpKwargs(params (string name, object value)[] kwargs)
{
    foreach ((string name, object value) in kwargs)
    {
        Console.WriteLine($"{name}: {value}");
    }
}

If you want kwargs as a Dictionary do this:

Dictionary<string, object> kwargsAsDictionary = kwargs.ToDictionary(x => x.name, x => x.value);

Usage:

GetKwargs(("intParam", 1), ("stringParam", "2"));
//  Output will this:
//    intParam: 1
//    stringParam: 2
0x01010
  • 168
  • 12
0
void paramsExample(object arg1, object arg2, params object[] argsRest)
{
    foreach (object arg in argsRest)
    { /* .... */ }
}

call it like this,

paramsExample(1, 0.0f, "a string", 0.0m, new UserDefinedType());
Adeel Ahmed
  • 1,591
  • 8
  • 10
0

You can use a param list as your final argument to the function, like this:

void paramsExample(object arg1, object arg2, params object[] argsRest) {
    foreach (object arg in argsRest)
    { /* .... */ }
}

the method can be invoked with any number of arguments of any type.

paramsExample(1, 0.0f, "a string", 0.0m, new UserDefinedType());

This is a strongly typed argument so if I wanted to use a list of strings I could do this:

 public string Concat(string separator, params string[] strings) {
      string result = "";
      for (int i = 0; i < strings.Length; i++) {
          if (i > 0)
              result += separator;
          result += strings[i];
      }    
      return result;
 }

To invoke:

MessageBox.Show(Concat("+", "Anders", "Eric", "Scott", "Duncan") + " = great team");

Check out more info here: http://blogs.msdn.com/b/csharpfaq/archive/2004/05/13/how-do-i-write-a-method-that-accepts-a-variable-number-of-parameters.aspx

Chris McCabe
  • 1,010
  • 11
  • 20