如何使用LINQ初始化一个由重复元素组成的数组?

11

目前,我正在使用类似以下的方法来构建一个由10个对象组成的列表:

myList = (from _ in Enumerable.Range(0, 10) select new MyObject {...}).toList()

这是基于我的 Python 背景,我会写成:

myList = [MyObject(...) for _ in range(10)]

请注意,我希望我的列表包含10个对象的实例,而不是同一个实例重复10次。

在C#中,这种做法仍然合理吗?相比简单的for循环,这种做法是否会有额外的代价?


你在MyObject中使用了Range值吗,还是只需要任意对象? - Tilak
@Tilak:任意对象(因此使用了下划线) - Eric
在我看来,使用简单的for循环比使用LINQ查询略微更好(可读性/性能)。 - Tilak
这种方式相比简单的for循环有什么代价吗?是的,循环执行了3次,这是唯一的代价。 - Tilak
@tilak:当调用.toList时,循环肯定只执行一次。 - Eric
3个回答

12

在这种情况下,Fluent API看起来更易读,但很难看出你的代码意图:

var list = Enumerable.Range(0, 10).Select(_ => new MyObject()).ToList();

简单的if循环快速易懂,但也隐藏了意图 - 创建一个包含10个项目的列表。
List<MyObject> list = new List<MyObject>();
for (int i = 0; i < 10; i++)
     list.Add(new MyObject());

最适合提高可读性的是建造者模式,它可以描述您的意图。
public class Builder<T>
    where T : new()
{
    public static IList<T> CreateListOfSize(int size)
    {
        List<T> list = new List<T>();
        for (int i = 0; i < size; i++)
            list.Add(new T());
        return list;
    }
}

使用方法:

var list = Builder<MyObject>.CreateListOfSize(10);

这个解决方案与简单的循环一样快,意图也非常明确。在这种情况下,我们需要编写的代码最少。


1
很确定 Enumerable.Range(0, 10) 会返回11个值。 - Eric
3
@Eric 相当确定第二个参数是 count - Sergey Berezovskiy
如果您只需要使用 T 的默认构造函数,那么一切都很好,否则传递一个构造委托可能更可取。 - spender
@spender 同意,另外 (T)Activator.CreateInstance(typeof(T), args) 是创建实例并将参数传递给构造函数的选项。 - Sergey Berezovskiy
我认为在这种情况下意图并不明确。 - Weipeng
@SergeyBerezovskiy 谢谢您的回答!为什么在构建器中您使用了IList而不是List作为返回类型? - Matthew

5
您可以尝试:
Enumerable.Range(0, 1000).Select(x => new MyObject()).ToArray();

4

个人认为Enumerable.Repeat缺少一种重载方法。 有一个很方便的补充,类似于这样:

public static class EnumerableEx
{
    public static IEnumerable<T> Repeat<T>(int amt, Func<T> producer)
    {
        for(var i = 0; i < amt; ++i)
        {
            yield return producer();
        }
    }
}

所以你可以...
EnumerableEx.Repeat(10, () => new object()) //.ToList()

重载是不够的 - 你不能创建一个重复10次函数的可迭代对象。 - Eric
忘掉那个 - 你可以通过返回类型区分这两种情况。 - Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接