Create objects within a List without for / foreach C #

9
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, is there any other way to create N objects within a list of the same type?

EDIT

All forms that are in the answers work in the same or similar way. However, as mentioned by @bigown there is a huge performance problem in all of them when compared to for .

Test conducted by @bigown in .NET Fiddle

    
asked by anonymous 29.09.2015 / 14:43

5 answers

7

Answers that use Enumerable.Repeat have a potential problem: it creates a values replay, 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 and not copies of the object will actually be made.

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

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

Solution that works for both reference types and value types:

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

Or:

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

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

Edit:

I agree on grade and gender with the @bigown edition that says the original AP code is the best option; not because of performance but because it is more expressive and everything in it reveals only the real intention without having to create for example an array that only serves as artifice to guarantee the iterations.

I would still add the number of purchases in the list builder and maybe it would be further: return an array instead of returning a list, returning in the method a IList<Compra> or a IEnumerable<Compra> (but also has to see the side of the consumer code - which is more convenient for him).

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;
}

So my option using LINQ is just to answer directly the question (which wants to dispense with the for statement) and to warn about using Enumerable.Repeat .

    
29.09.2015 / 16:05
7

You did not use foreach , nor could you scan this list. Use 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;
}

See working on dotNetFiddle .

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

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

See dotNetFiddle that really works. And see how really problematic is to use the other way.

Since it had several answers and in the background the question asks for something that does not have so much need thus, I a fast test of performance. I know that performance should not always be punished, one should think about what is simple, what is readable. But performance can not be neglected in all cases.

What is simple and readable is not unanimous. For example, you have to find LINQ as simple and readable. There are those who find the traditional code better in this respect.

In the test I concluded that the original AP code is the fastest with a large margin compared to the others.

As the fastest and simplest in my opinion, I would stick with the original AP solution. And it's the advice I give him, do not change.

Test run in dotNetFiddle .

I just want to remind you that testing on a public machine like this has little reliability. I recommend running on your computer under more controlled conditions.

    
29.09.2015 / 14:46
4

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

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 well at the time I was posting the answer and my response was almost the same as @bigown, here's an alternative.

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

EDIT 2

As stated in @ Caffé's answer, Enumerable.Repeat() causes all items within the List to be the same item. So if you need the items in the list not to point to the same place (which is likely to be very likely), here's an alternative.

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

See working at dotNetFiddle

    
29.09.2015 / 14:48
2

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

private List<Compra> CriarCompras(int numComprasParaGerar)
{
    return new List<Compra>(Enumerable.Range(0, numComprasParaGerar).Select(i => new Compra()));
}   
    
29.09.2015 / 15:11
0

In addition to the other answers, you can also do this 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);
}
    
29.09.2015 / 18:08