【问题标题】:How to lazy initialize generic List<T> via class extension如何通过类扩展延迟初始化泛型 List<T>
【发布时间】:2011-12-26 19:45:51
【问题描述】:

我正在尝试为 List 类编写 safeAdd 扩展函数,所以如果它尚未初始化 - 初始化它并添加新值。但是在我从扩展方法返回后,我刚刚初始化的列表等于 null。怎么了?

测试类:

private class Test 
{
    public Test()
    {
        Id = Guid.NewGuid();
        //items = new List<string>();
    }
    public Guid Id { get; set; }
    public List<string> items { get; set; }
}

扩展类:

public static class Helpers
{
    public static void safeAdd<T>(this List<T> list, T item)
    {
        if (list == null)
            list = new List<T>();
        list.Add(item);
    }
}

主要部分:

Test t = new Test();
t.items.safeAdd("testWord");
//Here t.items == null; WHY?

【问题讨论】:

    标签: c# .net generics


    【解决方案1】:

    你只分配了一个局部方法变量(它只存在于扩展方法中)——这对调用set没有任何作用,老实说,没有方便的方法来访问@ 987654322@ 和 set 不冗长,或使用大量反射/Expression 代码。 IMO 只是在课堂上做:

    private List<string> items;
    public List<string> Items { get {
         return items ?? (items = new List<string>()); } }
    

    这将在第一次访问时自动初始化并分配列表。

    除非我同步,否则它不会是线程安全的,或者(也许更可取)使用Interocked.CompareExchange 进行分配。查看联锁 它可以很容易地成为线程安全的,但这很少是实例方法的要求,并且有相关的开销

    【讨论】:

    • 但是不仅ValueTypes被克隆到一个方法中了吗?我认为我们应该只为它们使用refout 修饰符..
    • @Jentor 除非另有说明,否则所有内容均按值传递,包括按值传递引用的引用类型,因此请更改参数指向的内容,不要更改传递的参数指向的内容,您不能使用参考这个,所以没有直接的解决方法
    • 有什么选择的理由??是因为懒还是只是出于习惯?
    • 那个??据我所知,基于IL的方法不是线程安全的(虽然找不到证明或反驳的文档)。在这种特殊情况下,这并不是一个真正的问题,因为 List 实例成员也不是线程安全的。因此,我的问题是看看是否有类似的论点?解决了让你选择 Lazy 的问题?在懒惰
    • @RuneFS 实际上,除非我同步,否则它不会是线程安全的,或者(也许更可取)使用 Interocked.CompareExchange 进行分配。查看Interlocked 它可以很容易地实现线程安全,但这很少是实例方法的要求,并且有相关的开销
    【解决方案2】:

    如果您使用的是 .NET 4.0,则可以使用 Lazy 类自动处理延迟初始化。然后你的代码变成:

    private class Test 
    {
        public Test()
        {
            Id = Guid.NewGuid();
            //items = new List<string>();
        }
        public Guid Id { get; set; }
        private Lazy<List<string>> _items = new Lazy<List<string>>();
        public List<string> items 
        { 
            get { return _items.Value; }
        }
    }
    

    然后您可以随时致电Test.items.Add。它将在首次使用时初始化。

    更多信息请参见Lazy Initialization

    【讨论】:

    • 正如我在上一个答案中提到的,MongoDB C# 驱动程序无论如何都会序列化_items 对象,即使它是空的。但我想获得使用[BsonIgnoreIfNull] 属性的所有魅力。
    【解决方案3】:

    使用新 C# 功能的更紧凑的方式

    private List<string> _items;
    public List<string> Items
    {
       get => _items ??= new();
       set => _items = value;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-17
      相关资源
      最近更新 更多