Create objects within a list without for/foreach C#

Asked

Viewed 1,656 times

11

private List<Compra> CriarCompras(int numComprasParaGerar)
{
    List<Compra> lstCompras = new List<Compra>();
    for (int i = 0; i < numComprasParaGerar; i++)
       lstCompras.Add(new Compra());

    return lstCompras;
 }

Following the example, there is some other way to create N objects within a list of the same type?

EDIT

All the forms that are in the answers work equally or similarly. However, as quoted by @Maniero there is a huge performance problem in all of them when compared to the for.

Test performed by @Maniero on . NET Fiddle

  • 1

    The answers they use Enumerable.Repeat have a problem: it creates a repeat of values, that is, the reference to the object passed by parameter. So that all items in the list will be the same item. That is, a single instance of Compra and the reference to this instance will be copied for each item in the list - changing one of the purchases, all changes.

5 answers

9

You didn’t use a foreach, and could not even scan this list. Use the Enumerable.Repeat() which is a LINQ method.

private List<Compra> CriarCompras(int numComprasParaGerar) {
    var lstCompras = new List<Compra>();
    lstCompras.AddRange(Enumerable.Repeat(new Compra(), numComprasParaGerar));
    return lstCompras;
}

Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

Since everyone has arranged, to answer not to be wrong I will choose the solution that I found more elegant to solve the question.

private static List<Compra> CriarCompras(int numComprasParaGerar) => new int[numComprasParaGerar].Select(i => new Compra()).ToList();

Really see working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

Behold how problematic it is in ideone doing otherwise. And in the .NET Fiddle. Also put on the Github for future reference.

Since you’ve had a lot of answers, and at the bottom of the question, you ask for something that you don’t really need that much, I need a quick performance test. I know that one should not always suffer in performance, one should think about what is simple, what is readable. But performance cannot be neglected in all cases.

What is simple and readable is not unanimous. For example, you have to find the LINQ as simpler and readable. Some people think the traditional code is better in this respect.

In the test I concluded that the original code of the AP is the fastest with wide margin in relation to the others.

Because it is the fastest and simplest in my opinion, I would stick with the original solution of the AP. And it is the advice I give to him, do not change.

Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

I just want to remind you that the test on such a public machine has little reliability. I advise running on your computer in more controlled conditions.

  • Sorry for the error in the question, I edited the same.

  • In this case, since you know the number of elements before hand, it would not be beneficial to pass the number of elements in the constructor in order to avoid another allocation?

  • 1

    @Omni usually yes, in this case it won’t make a difference because it’s only using one AddRange then. If you had used the for as he was doing, it would be highly interesting to use the constructor that already allocates the space needed for all elements and not create the problem of Shlemiel the Painter

  • I had already done these tests. Result: I kept the original shape, with for.

  • @Jonathanbarcela My answer came back to be accepted by you so :D I’m not asking to change the acceptance, but in fact what you decided to do is what is in my answer. I almost put this right at the beginning, even without doing performance tests. I would opt for this just because it is the simplest.

  • @Funny Moustache :D His original response caused problems to AP because he ignored that Enumerable.Repeat just copy values, remember?

  • @Caffé and thence, the current was the one he actually chose. If I had gone in my gut, the answer would be exactly, stick to your code, but I thought you’d consider an answer that doesn’t answer. But it can be accepted, I don’t want it. For me it is enough that he made the right choice, even if the green doesn’t show.

  • Lol (really laughing here hehehe) By not having followed his infallible instinct or for any other reason, the fact is that you offered an answer with errors and learned it after you read my answer. That’s what I’m here for: to learn, to have fun and to help. If points were of interest to me, I was crushed - it would only be frustration. For example, you received 5 votes for a reply that doesn’t work and while it still didn’t work :D (and of course, you must have received far fewer points for other more relevant answers). It’s Always good to talk to you @bigown (still Lol )

Show 3 more comments

9


The answers they use Enumerable.Repeat have a potential problem: it creates a repeat of values, that is, if the object passed by parameter is a Reference type (a class, for example), copies of the reference to the object passed by parameter will be made, not copies of the actual object.

So that all items in the list would be the same item. That is: if Compra is a class, a single instance of Compra and the reference to this instance will be copied for each item in the list - changing one of the purchases, all changes.

If this is a problem for you, I present below an alternative that actually creates X different instances.

Solution that works so much for Reference types how much to value types:

private List<Compra> CriarCompras(int numComprasParaGerar)
{
    return (from i in new int[numComprasParaGerar] select new Compra()).ToList();
}

Or else:

private List<Compra> CriarCompras(int numComprasParaGerar)
{
    return new int[numComprasParaGerar].Select(i => new Compra()).ToList();
}

(I prefer the first option, I find more expressive).

Edit:

I agree in degree and gender with the @Maniero edition that says that the original AP code is the best option; not for performance but because it is more expressive and everything in it reveals only the real intention without having to create for example a array which only serves as a ruse to ensure iterations.

I would still add the number of purchases in the list constructor and perhaps go further: return an array instead of returning a list, returning in the one method IList<Compra> or a IEnumerable<Compra> (but it also has to do with the consumer code - which is more comfortable for it).

I would therefore use a code something like this:

private IList<Compra> CriarCompras(int qtdComprasParaGerar)
{
    IList<Compra> compras = new Compra[qtdComprasParaGerar];

    for (int i = 0; i < qtdComprasParaGerar; i++)
        compras[i] = new Compra();
    return compras;
}

Therefore, my option using LINQ is only to answer directly the question (who wants to dispense with the statement of the for) and to warn about the use of Enumerable.Repeat.

  • 1

    Very, very, very well observed.

  • His answer saved lives now kkk I was doing the other ways and trying to edit the purchases, did not know that was created a same reference of the object. It really got into trouble. Even though he had several valid answers, his answer was what gave rise to the editing in all the others, so I choose his. Vlw :D

6

Yes, you can use the method AddRange() and Enumerable.Repeat() for that reason.

private List<Compra> CriarCompras(int numComprasParaGerar)
{
    var lista = new List<Compra>();
    lista.AddRange(Enumerable.Repeat(new Compra(), numComprasParaGerar);    
    return lista;
}

EDIT

As my internet failed right around the time I was posting the answer and my response was pretty much the same as @Maniero, here’s an alternative.

private List<Compra> CriarCompras(int numComprasParaGerar)
{
    return Enumerable.Repeat(new Compra(), numComprasParaGerar).ToList();
}

EDIT 2

As was said in the reply of @Caffé o Enumerable.Repeat() makes all items within the List the same item. So, if you need list items not to point to the same place (which is much probable), here is an alternative.

private static List<Compra> CriarCompras(int numComprasParaGerar)
{
    return new List<Compra>(Enumerable.Range(0, numComprasParaGerar).Select(i => new Compra()));
}

See working on dotNetFiddle

4

Can use Enumerable.Range() to generate a sequence of values and with a .Select() create the new instances and finally pass them on to the list constructor as follows:

private List<Compra> CriarCompras(int numComprasParaGerar)
{
    return new List<Compra>(Enumerable.Range(0, numComprasParaGerar).Select(i => new Compra()));
}   
  • Go to edit the answer, but invoke the method .ToList() (as in Jeferson’s reply), does the same as my answer (i.e., creates a new list and uses the constructor).

2

In addition to the other responses, it is also possible to do using recursion:

private List<Compra> CriarLista(int numComprasParaGerar)
{
    var lstCompras = new List<Compra>();
    CriarCompras(lstCompras, numComprasParaGerar);
    return lstCompras;
}

void CriarCompras(List<Compra> lstCompras, int numComprasParaGerar)
{
   lstCompras.Add(new Compra());

   if(lstCompras.Count < numComprasParaGerar)
       CriarCompras(lstCompras, numComprasParaGerar);
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.