2

Is there a more efficient way of doing this.

the function:

public void someFunction(Dictionary<string, object> args){
   foreach (var item in args){
       Console.WriteLine("key: " + item.Key + " value: " + item.Value);
   }
}

Calling the function what doesn't look nice: <-- here is the problem

someFunction(new Dictionary<string, object>
        {
            { "number", 3 },
            { "text", "Some Text" }
}));

Output:

key: number value: 3
key: text value: Some Text

I want to achieve something like this where I can always change the passing variable names and values

someFunction(["number" => 3, "text" => "Some Text"]);

I want to pass a string name and an object type array to the function so I can iterate through it. This code works but I want it to look neater.

In PHP there is http://php.net/manual/en/function.compact.php

Which makes it able to have the name of the variable and the value in an array usable in your code. With c# I have to use a whole dictionary.

I would like to see a more efficient way.

Edit: THANK YOU ALL. Didn't know I would get answers so fast 0_0

Mo D Genesis
  • 5,187
  • 1
  • 21
  • 32
  • you can use a MultiValue Dictionary.. for example `Dictionary>` or a Class, and in your case you will only iterate a `List` – MethodMan Dec 09 '17 at 21:23
  • I don't think you can iterate a dictionary directly, but you can iterate it's Keys collection. Are you having performance problems with the dictionary? – Zohar Peled Dec 09 '17 at 21:26
  • 1
    you couse use a class... – Daniel A. White Dec 09 '17 at 21:26
  • If you just need something to itrate through you should not use a dictionary to combine a string with some object. This could be done with a list of a struct where inserting is not that slow. – florgeng Dec 09 '17 at 21:27
  • 1
    My problem is with calling the function. It takes more lines which doesn't look nice where as in php I can do everything in one line. – Mo D Genesis Dec 09 '17 at 21:28
  • @MoDGenesis don't forget to mark the answer that helped you the most – Icepickle Dec 10 '17 at 20:08

5 Answers5

3

Sure, you can use the params keyword for allowing a dynamic number of arguments

You could then for example do (it doesn't have to be a KeyValuePair<string, object>, it could be a Tuple<string, object>)

public void AddSomeKeys(params KeyValuePair<string, object>[] arguments) {
    foreach (var item in arguments){
        Console.WriteLine($"key: {item.Key} value: {item.Value}");
    }
}

In C# 7 you could make it even a bit easier, by making use of the ValueTuple

public void AddSomeKeys(params (string key, object value)[] arguments) {
    foreach (var item in arguments){
        Console.WriteLine($"key: {item.key} value: {item.value}");
    }
}

Or you could use the more anonymous ValueTuple

public void AddSomeKeys(params (string, object)[] arguments) {
    foreach (var item in arguments){
        Console.WriteLine($"key: {item.Item1} value: {item.Item2}");
    }
}

In both cases, you could call the methods like:

AddSomeKeys(new KeyValuePair<string, object>( "item1", "value1" ), new KeyValuePair<string, object>( "item2", "value2" ) );

Or with the second version

AddSomeKeys( ( key: "item1", value: "value1" ), ( key: "item2", value: "value2" ) );

Or with the third version

AddSomeKeys( ( "item1", "value1" ), ( "item2", "value2" ) );
Icepickle
  • 12,689
  • 3
  • 34
  • 48
3

If you're just looking for sparse syntax for the caller, you can use an anonymous type, similar to the way that jquery passes optional options around. Example:

public static void someFunction(object input)
{
    foreach (var p in input.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
             Console.WriteLine("{0}={1}", p.Name, p.GetValue(input));
}

The function itself is a little more messy, but for the caller, the syntax is pretty lightweight:

someFunction( new { Number = 3, Text = "Some text"});

output:

Number=3
Text=Some text

If you plan to do this a lot, you can make it a little less painful with an extension method. I named mine Extract(), below:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;

public static class ExtensionMethods
{
    public static Dictionary<string, object> Extract<T>(this T input) where T : class
    {
        return input
            .GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .ToDictionary( p => p.Name, p => p.GetValue(input));
    }
}

public class Program
{
    public static void someFunction(object input)
    {
        var parameters = input.Extract();  //Magic

        Console.WriteLine("There were {0} parameters, as follows:", parameters.Count);
        foreach (var p in parameters)
        {
            Console.WriteLine("{0}={1}", p.Key, p.Value);
        }
    }

    public static void Main()
    {
        someFunction(new { number = 3, text = "SomeText" } );
        someFunction(new { another = 3.14F, example = new DateTime(2017, 12, 15) } );
    }
}

Output on DotNetFiddle:

There were 2 parameters, as follows:
number=3
text=SomeText
There were 2 parameters, as follows:
another=3.14
example=12/15/2017 12:00:00 AM

The main downside with this approach is that you can't use interfaces or do anything to require that certain keys be present. Also, you have to use Reflection, which is a little bit more work. If you'd like to avoid these downsides, you could use the same approach but with a non-anonymous type, like this:

class SomeFunctionParams
{
    public int Number { get; set; }
    public string Text { get; set; }
}

public static void someFunction(SomeFunctionParams params)
{
    Console.WriteLine("Number={0}", params.Number);
    Console.WriteLine("Text={0}", params.Text);
}

...but then you'd lose the dynamic advantages of the anonymous type.

Another option is expose every possible key as an optional parameter, and only provide the ones you want:

void someFunction(int Number = default(int), string Text = default(string), DateTime SomeOtherParam = default(DateTime))
{
    if (Number != default(int)) Console.WriteLine("Number={0}", Number);
    if (Text != default(string)) Console.WriteLine("Text={0}", Text);
    if (SomeOtherParam != default(DateTime)) Console.WriteLine("SomeOtherParam={0}", SomeOtherParam);
}

someFunction(Number : 3, Text : "Some text");
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • Personally, I like it, as it also assures that you are not sending the same keys, and it looks the most like the implementation the OP wanted. However, I would personally like to see a bit more information about the downsides this method has – Icepickle Dec 09 '17 at 21:45
  • 1
    Could you also say how to select count() from the object type? Also with the second option, you lose the ability to change the variable names. – Mo D Genesis Dec 12 '17 at 09:43
  • 1
    This work around is legendary. Seems like that if I wanted to come up with it I needed more knowledge in c# then I have now. (reflection etc.) hence, why I asked how to do count() – Mo D Genesis Dec 12 '17 at 15:23
1

C# 6 added a new syntax that makes inline initialization slightly easier to read:

someFunction(new Dictionary<string, object> {
    ["number"]=3, ["text"]="Some Text"
});
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0

How about:

public static void Main()
{
    Iterate(new Dictionary<string, object> { ["1"] = "one", ["2"] = "Two" });
    Console.Read();
}

public static void Iterate(Dictionary<string, object> args)
{
    foreach (var item in args)
    {
        Console.WriteLine("key: " + item.Key + " value: " + item.Value);
    }
}
CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
0

You can use alias in the begining of the file:

using D = System.Collections.Generic.Dictionary<string, object>;

or

using System.Collections.Generic;

Namespace {
    using D = Dictionary<string, object>;

and then use D instead of Dictionary<string, object> :

public void someMethod(D args){
   foreach (var item in args){
       Console.WriteLine("key: " + item.Key + " value: " + item.Value);
   }
}

and

someMethod(new D { { "number", 3 }, { "text", "Some Text" } }));

If all of the arguments are known, it's much more efficient to use optional arguments :

void someMethod(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0){
    Console.WriteLine($"key: {nameof(a)} value: {a}");
    Console.WriteLine($"key: {nameof(b)} value: {b}");
    // ...
}

and sample use:

someMethod(d: 1, b: 2);
Slai
  • 22,144
  • 5
  • 45
  • 53