使用Linq替换集合中的一个元素

4
在以下特定场景中,我该如何使用Linq查找并替换属性:
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
    public Property[] Properties { get; set; }

    public Property this[string name]
    {
        get { return Properties.Where((e) => e.Name == name).Single(); }
        //TODO: Just copying values... Find out how to find the index and replace the value 
        set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
    }
}

感谢您提前帮助。

你使用的是哪个PropertyBag?我认为在.Net BCL中没有这样的一个。我问这个问题是因为“Properties” getter可能正在执行克隆操作,这就是为什么你不能做你试图做的事情的原因。 - Jonathan Rupp
Jonathan,这是我自己的自定义PropertyBag类。上面的代码之所以有效,是因为我没有替换Property[]数组中的整个项。代码中的注释说明了: //TODO:只是复制值...找出如何查找索引并替换值 - Vyas Bharghava
我想知道为什么你没有使用我的不带LINQ的IndexOf()解决方案...现在我知道了!我将我的答案从实例更正为静态方法。 - Daniel Brückner
2个回答

6
不要使用LINQ,因为它不会改善代码,因为LINQ是设计用于查询集合而不是修改它们。我建议采用以下方法。
// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);

if (index != -1)
{
   this.Properties[index] = value;
}
else
{
   throw new ArgumentOutOfRangeException();
}

为什么Array.Sort()和Array.IndexOf()方法是静态的?

此外,建议不要使用数组。考虑使用IDictionary<String,Property>。这将简化代码如下。

this.Properties[name] = value;

请注意,这两种解决方案都不是线程安全的。


一个临时的LINQ解决方案 - 你看,你不应该使用它,因为整个数组将被替换为一个新数组。

this.Properties = Enumerable.Union(
   this.Properties.Where(p => p.Name != name),
   Enumerable.Repeat(value, 1)).
   ToArray();

6
不要使用LINQ,因为它不会改善代码。这个陈述没有解释是无用的。 - spender
@danbruc:不幸的是,使用[I]Dicitonary不是一个选项,因为它不可序列化...只能把它留成数组...是的...IndexOf()似乎是一个合理的解决方案...但我只是希望可能是我做错了什么,确实有一种在Linq中实现这个的方法... - Vyas Bharghava

0
你的 'Property' 是一个类还是结构体?这个测试对我来说通过了。
public class Property
{
    public string Name { get; set; }
    public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
    public Property[] Properties { get; set; }

    public Property this[string name]
    {
        get { return Properties.Where((e) => e.Name == name).Single(); }
        set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
    }
}

[TestMethod]
public void TestMethod1()
{
    var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
    Assert.AreEqual("Y", pb["X"].Value);
    pb["X"] = new Property { Name = "X", Value = "Z" };
    Assert.AreEqual("Z", pb["X"].Value);
}

我不得不想知道为什么getter返回一个“Property”而不是任何数据类型的.Value,但我还是很好奇你看到的结果与我看到的不同。


这是一个类...从上面复制的注释:上面的代码可以工作,因为我没有替换Property[]数组中的整个项。代码中的注释说明了这一点://TODO: 只是复制值...找出如何查找索引并替换值。 - Vyas Bharghava
你能贴入更多的代码吗?我贴的代码在我的电脑上是可用的,我只能想到这可能是导致你描述的问题的唯一原因。 - Jonathan Rupp
修改集合。您必须构建一个新的集合,并用它替换旧的集合 - 请参见我的答案中的示例 - 但这不是一个明智的解决方案。 - Daniel Brückner
啊,我明白真正的问题是什么了。是的,你不能用LINQ修改集合。它是语言集成的查询,而不是更新。 - Jonathan Rupp
嗯,有List<T>.FindIndex,但它不适用于Array(我猜ToList()会有点违背初衷)。话虽如此,如果你真的想要,在数组上实现FindIndex,你应该能够编写一个自定义扩展方法。 - Jonathan Rupp
显示剩余3条评论

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