【问题标题】:Cannot modify the return value of 'System.Collections.Concurrent.ConcurrentDictionary无法修改 'System.Collections.Concurrent.ConcurrentDictionary 的返回值
【发布时间】:2014-11-26 22:03:11
【问题描述】:

我在尝试修改 ConcurrentDictionary 中的项目时遇到非常奇怪的错误:

private ConcurrentDictionary<string, Tripple> SeenEnoughDict =   
               new ConcurrentDictionary<string, Tripple>();  
private struct Tripple
{
    public int prev_antenna;
    public int num_of_times_seen;
    public Timer timer;

    // ctor
    public Tripple(int antenna, Timer tm)
    {
        this.prev_antenna = antenna;
        this.num_of_times_seen = 1;
        this.timer = tm;
    }
} 
// several items were added to the dictionary

 Parallel.ForEach(_myReaderTC.Cast<Tag>(), t => {
     // attempting to modify the item  
     // t.ID is string    
     SeenEnoughDict[t.ID].num_of_times_seen = SeenEnoughDict[t.ID].num_of_times_seen + 1; 
}

最后一行抛出错误:

Error 149   Cannot modify the return value of  
'System.Collections.Concurrent.ConcurrentDictionary<string,Tripple>.this[string]'   
because it is not a variable

这个错误的有趣之处在于http://pastebin.com/0cQJMcUD 可以正常工作。 最近,我将我的解决方案从 2010 年转换为 2013 年。在 2010 年,我使用了从 .NET 4 向后移植到 3.5(我从 NuGet 获得)的并发集合。

【问题讨论】:

  • 尝试将返回值设为一个类。我会是你可以修改它的值。编译器在这里为您提供帮助,因为您正在返回一个结构并对其进行修改,您不会像您认为的那样获得值更新。
  • 由于这个原因,创建可变结构通常不是一个好主意。我建议您使用由私有 fields 支持的公共 properties,而不是公开公开这些字段 - 并开始遵循 .NET 命名约定。
  • @focuspark - 如果你能把它写成答案。我很乐意接受。
  • @newprint Adam Robinson 在下面写了一篇很棒的文章,供您标记为答案。无论如何谢谢:-)

标签: c# .net concurrency


【解决方案1】:

这是因为您的类型是struct。一般来说,除非您知道创建值类型(struct)有特定原因,否则您实际上应该创建class。如果您打算修改它的某些内容(即您所创建的内容并不代表谨慎的“价值”,并且更改某些内容并不会使它本质上成为其他内容),那么您应该绝对使用class。在解决您的问题方面,只需将 struct 替换为 class 即可。

但是,我还建议公开属性而不是字段。 .NET 语言中的一般习惯用法是使用private 支持字段,并在必要时通过使用属性将它们暴露在声明类之外。所以不要这样:

public class Foo
{
    public int MyValue;
}

你会这样做:

public class Foo
{
    private int myValue;

    public int MyValue
    {
        get { return myValue; }
        set { myValue = value; }
    }
}

我意识到这有点罗嗦,但是对于简单的属性(简单的 get/set 操作,除了设置所需字段的值之外没有其他任何东西)你可以使用“自动属性”

public class Foo
{
    public int MyValue { get; set; }
}

对于简单的 get/set 操作,属性语法不再冗长,但仍然为您提供了属性为您提供的灵活性和关注点分离。

最后,我建议采用 PascalCase 名称,因为这是大多数 .NET 语言中的名称。

完成所有这些更改后,您的课程将如下所示:

private class Tripple
{
    public int PrevAntenna { get; set; }
    public int NumOfTimesSeen { get; set; }
    public Timer Timer { get; set; }

    // ctor
    public Tripple(int antenna, Timer tm)
    {
        this.PrevAntenna = antenna;
        this.NumOfTimesSeen = 1;
        this.Timer = tm;
    }
} 

我觉得您的班级可以使用更有意义的名称(除非“Triple”在您的工作中具有某些我不知道的行业特定含义),但希望这足以提供帮助.

【讨论】:

  • 感谢您的详细解答。我在想structs 的工作方式与它们在 C/C++ 中的工作方式相同。我完全错了!
猜你喜欢
  • 2010-12-17
  • 2012-01-20
  • 1970-01-01
  • 2012-07-07
  • 1970-01-01
  • 2013-10-22
  • 2014-03-01
  • 2016-08-03
  • 1970-01-01
相关资源
最近更新 更多