2

My problem lies in that I have a method that takes a variable amount of parameters.

Each of those parameters are an object, the real problem lies in that it gets horribly verbose to write new ClassName(p1, p2) for every single parameter in that method.

is there a way to send p1 and p2 as a single parameter in the form of either {p1, p2} or (p1, p2)?

so that I can write Insert(("John", "Doe"), ("Sherlock", "Holmes"), ... etc) and then pass those into news in the method itself rather than writing Insert(new Person("John", "Doe"), new Person("Sherlock", "Holmes"), ... etc)

I know tuples in F# and scala can do it this way, but using tuples in C# just makes for even longer code

so is there a way to make it less verbose?

Edit: I'm not looking to create new arrays or new lists instead I want to avoid the new keyword as much as possible

Edit2: some people requested to see what my Insert method looks like; currently it looks like this:

public void Insert(params Person[] arr)
{
    //inserts the person in a hash table
    Action<Person> insert = (person) => _table[hasher(person.Name)].Add(person);

    // calls the insert function/action for each person in the parameter array
    Array.ForEach(arr, insert);
}
Electric Coffee
  • 11,733
  • 9
  • 70
  • 131
  • 2
    show your code please – cuongle May 06 '13 at 10:18
  • 1
    Although not quite what you ask, collection initializers can do this sort of thing at construction: http://stackoverflow.com/a/2495801/14357 – spender May 06 '13 at 10:18
  • @spender His object doesn't sound like a collection. Just an object with some properties. And you (OP) should leave it the way that it is now. They are two different parameters, and they are ought to stay that way. – SimpleVar May 06 '13 at 10:20
  • 2
    It sounds like you should just accept this verbosity, but if you just can't, then here is a very silly (but less verbose!) option: `public static Person _(string f, string n) { return new Person(f,n); }`. Now your call-site is `Insert(_("John", "Doe"), _("Some", "Guy"));` Yay! – dlev May 06 '13 at 10:33

5 Answers5

5

You could make a collection that supports initializer syntax and provide that as a parameter for your methods. This would allow the following:

void Main()
{
    SomeMethod(new PersonCollection{{1, 2}, {3, 4}, {5, 6}});
}
void SomeMethod(PersonCollection pc)
{
}

//...
class Person
{
    public Person(int a, int b)
    {
    }
}
class PersonCollection:IEnumerable
{
    IList<Person> personList = new List<Person>();
    public void Add(int a, int b)
    {
        personList.Add(new Person(a,b));
    }

    public IEnumerator GetEnumerator()
    {
        return personList.GetEnumerator();
    }
}

All that's required to support such construct is a suitable void Add method and implementing of IEnumerable.

spender
  • 117,338
  • 33
  • 229
  • 351
  • As commented, I believe this is logically wrong, because as far as I understand - his object is not a collection in any way. I consider this a hack in this case. – SimpleVar May 06 '13 at 10:26
  • 1
    I know this is a min-sample, but I have two questions: 1. Why not implement `IEnumerable`? 2. (More important) Why do you consider people to consist of two numbers? :) – dlev May 06 '13 at 10:26
  • @dlev If going that road, `IEnumerable` would indeed suit better. Other than that, he just gave an example for how to implement the idea. The content is irrelevant. – SimpleVar May 06 '13 at 10:28
  • They consist of 2 parameters that I knocked up in my sample code because I'm lazy and couldn't be bothered to refer to the question from my LinqPad session. The reason I'm only supporting IEnumerable is because this is the minimal case for initializer syntax to function correctly. Of course, if the OP wants more specific code, they can implement it themselves. I'm not sure this is logically wrong, as it provides a very quick and easy way to create a bunch of Person instances to pass on to a method. Might take some refactoring, but it's definitely in the spirit of the OPs question. – spender May 06 '13 at 10:30
  • This is the solution that by far gets closest to solving my problem But I can't help but wonder if there's an even less verbose way... – Electric Coffee May 06 '13 at 10:42
  • 1
    I like this solution the best. Also, if you add to PersonCollection this method: `public static implicit operator Person[](PersonCollection people){return people.personList.ToArray();}` then you can pass the `PersonCollection` directly to the original `Insert(params...)` method. – Matthew Watson May 06 '13 at 11:36
1

In C# there's no way of grouping other than classes and structs. If your demand of laconic code exceeds common sense, you can use "logical" methods. Call method like this:

Insert("John", "Doe", "Sherlock", "Holmes")

and handle it like this:

    void Insert(params string[] names)
    {
        if (names.Length % 2 != 0) throw new ArgumentException();

        for (int i = 0; i < names.Length; i += 2)
        {
            string name = names[i];

            string surname = names[i + 1];

            // use it
        }
    }
astef
  • 8,575
  • 4
  • 56
  • 95
  • @YoryeNathan Agree. But it is not verbose. Who knows, what are OP's priorities? – astef May 06 '13 at 10:28
  • I agree that it is fine for a novice, but even then, only if there is a real need to insert many "tuples" of parameters in that manner (pretty rare - usually params are for "1-sized tuples"). – SimpleVar May 06 '13 at 10:35
  • 1
    I don't like this as ensuring even numbers of parameters is not something that the compiler can check. Not safe. – spender May 06 '13 at 10:39
  • I agree with @spender on this one – Electric Coffee May 06 '13 at 10:40
1

You could write a helper method to deal with boilerplate code. So, supposing that want to pass several ClassName instances initialized with 2 arguments, you could write something like this:

public ClassName[] CreateClassNames(string[,] list)
{
     List<ClassName> result = new List<ClassName>()
     for ( int i = 0 ; i < list.GetLength(0); i++ )
     {
        result.Add(new ClassName(list[i][0], list[i][1]));
     }
     return result.ToArray();
}

And then call your Insert method:

Insert(CreateClassNames(new string[]{{"John", "Doe"}, {"Sherlock", "Holmes"}}))

Edit: Changed the argument to a 2 dimensional array in order to group the strings. Still not perfect, but better.

nmat
  • 7,430
  • 6
  • 30
  • 43
  • I don't like this as it relies on pairs of matched parameters that are unenforceable. It becomes like the cocoa dictionary methods. Painful and susceptible to errors that the compiler can't catch. – spender May 06 '13 at 10:31
  • yeah but the problem with this is that it'll be prone to errors because the names aren't explicitly grouped, so if I miss just one name I can get a segfault – Electric Coffee May 06 '13 at 10:39
  • @ElectricCoffee You're right. I changed it to a 2 dimensional array. It's a little more verbose, but enforces pairing. – nmat May 06 '13 at 10:45
  • as good as that is doesn't it only allow for 2 parameters? i.e. 2 people, the way my program is structured at the moment, I can add as many people as I please – Electric Coffee May 06 '13 at 10:56
1

Behold! I have created a monster!

It will let you write code like this:

Insert(new People()["John", "Doe"]["Sherlock", "Holmes"]["Fred", "Flintsone"]);

But really, it's an abomination isn't it?

using System;
using System.Collections.Generic;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            new Program().test();
        }

        void test()
        {
            Insert(new People()["John", "Doe"]["Sherlock", "Holmes"]["Fred", "Flintsone"]);
        }

        public void Insert(params Person[] persons)
        {
            foreach (var person in persons)
            {
                Console.WriteLine(person);
            }
        }
    }

    public class People
    {
        public static implicit operator Person[](People people)
        {
            return people.people.ToArray();
        }

        public People this[string firstName, string lastName]
        {
            get
            {
                people.Add(new Person(firstName, lastName));
                return this;
            }
        }

        private readonly List<Person> people = new List<Person>();
    }

    public class Person
    {
        public Person(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }

        public override string ToString()
        {
            return FirstName + " " + LastName;
        }

        public readonly string FirstName, LastName;
    }
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
-1

Please use dictionary as params.

xxxxxx
  • 1
  • 2