简体   繁体   English

为什么在C#中通过引用添加字典元素?

[英]Why are dictionary elements being added by reference in C#?

I am trying to do something that seems like it should be easy, but it is not working. 我正在尝试做一些看起来应该很容易的事情,但是没有用。 I have a dictionary objects with an int key. 我有一个带有int键的字典对象。 Within the objects, I have a property, PositionInEvent, that I want to match the key of the dictionary within it. 在这些对象中,我有一个属性PositionInEvent,它想与其中的字典键匹配。 It seems like this should be a simple loop operation, but it's not working. 看来这应该是一个简单的循环操作,但不起作用。 Here's what I have: 这是我所拥有的:

private void ensurePositions(ref Dictionary<int, DisplayUnit>  dict)
{
    var keys = dict.Keys.ToArray();
    foreach(var key in keys)
    {
        dict[key].PositionInEvent = key;
    }
}

When I run this on a dictionary of 5 objects with keys of 0-4 (it won't always be sequential like this, but I'm unit testing it), the PositionInEvent property on each item in the event has a value of 4. Every single one. 当我在键为0-4的5个对象的字典上运行此命令时(它不会总是像这样顺序运行,但是我正在对其进行单元测试),事件中每个项目的PositionInEvent属性的值为4每一个。 Why???? 为什么???? How can I do what I'm trying to do. 我该怎么做我想做的事。 It seems like it should be simple. 看起来应该很简单。

Update: 更新:

It was requested that I show how DisplayUnit is declared, instantiated, and added to the dictionary. 有人要求我显示DisplayUnit如何被声明,实例化并添加到字典中。

Here's the class declaration (I've taken out the stuff unrelated to instantiation and the property I'm working with here): 这是类声明(我已经取出了与实例化和此处使用的属性无关的内容):

/// <summary>
/// This is the base display unit from which all other units are derived.
/// </summary>
public abstract class DisplayUnit
{

    /// <summary>
    /// Initializes a new instance of the <see cref="AbstractClasses.DisplayUnit"/> class.
    /// </summary>
    protected DisplayUnit (Dictionary<string,string> attributes)
    {
        this.Id = Guid.NewGuid();
        tryApplyAttributes(attributes);
    }

    protected DisplayUnit(Guid id, Dictionary<string,string> attributes)
    {
        this.Id = id;
        tryApplyAttributes(attributes);
    }

    private void tryApplyAttributes(Dictionary<string,string> attributes)
    {
        string name;
        attributes.TryGetValue("Name", out name);
        Name = name;

        string description;
        attributes.TryGetValue("Description", out description);
        Description = description;

        string dateTime;
        attributes.TryGetValue ("DateCreated", out dateTime);
        DateTime date;
        DateTime.TryParse(dateTime,out date);
        DateCreated = date;

        string guid;
        attributes.TryGetValue("AssociatedEvent", out guid);
        Guid id;
        Guid.TryParse(guid, out id);
        AssociatedEvent = id;

        string group;
        attributes.TryGetValue("GroupId", out group);
        Guid groupId;
        var groupSet = Guid.TryParse(group, out groupId);

        string posInGroup;
        attributes.TryGetValue("PositionInGroup", out posInGroup);
        int intPos;
        var posSet = int.TryParse(posInGroup, out intPos);

        if (posSet && groupSet)
            UnitGroup = new DisplayUnitGrouping (intPos, groupId);

        string pos;
        attributes.TryGetValue("PositionInEvent", out pos);
        int position;
        int.TryParse (pos, out position);
        PositionInEvent = position;
    }

    public Guid Id {
        get;
        private set;
    }

    private int _positionInEvent;
    public int PositionInEvent {
        get{
            return _positionInEvent;
        }
        set {
            if (value < 0) {
                throw new NegativePositionException ("Position of DisplayUnit must be positive.");
            }
            _positionInEvent = value;
        }
    }

}

TextUnit is the class I'm actually using, which derives from DisplayUnit: TextUnit是我实际使用的类,它是从DisplayUnit派生的:

public class TextUnit : DisplayUnit
{
    public string Text {
        get;
        set;
    }

    public TextUnit (Dictionary<string, string> attributes) : base (attributes)
    {
        SetAttributes (attributes);
        Plugin = new FaithEngage.Plugins.DisplayUnits.TextUnitPlugin.TextUnitPlugin ();
    }


    public TextUnit (Guid id, Dictionary<string, string> attributes) : base (id, attributes)
    {
        SetAttributes (attributes);
    }


    #region implemented abstract members of DisplayUnit

    public override void SetAttributes (Dictionary<string, string> attributes)
    {
        string text;
        attributes.TryGetValue ("text", out text);
        Text = text;
    }

    #endregion
}

The dictionary being acted upon comes from here. 正在作用的字典是从这里来的。 _duRepo is a faked repository (see code below this). _duRepo是伪造的存储库(请参见下面的代码)。

public Dictionary<int, DisplayUnit> GetByEvent(Guid eventId)
{
    try {
        var returnDict = new Dictionary<int,DisplayUnit>();
        var dict = _duRepo.GetByEvent(eventId);
     if (dict == null)
            return null;
        foreach(var key in dict.Keys)
        {
            var du = _factory.ConvertFromDto(dict [key]);
            if(du == null) continue;
            returnDict.Add (key, du);
        }
        ensurePositions(ref returnDict);
        return returnDict;
    } catch (RepositoryException ex) {
        throw new RepositoryException ("There was a problem accessing the DisplayUnitRepository", ex);
    }
 }

This all comes from this unit test (which I can't get to pass, and I don't know why): 所有这些都来自这个单元测试(我无法通过,也不知道为什么):

[Test]
public void GetByEvent_ValidEventId_ReturnsDictOfEvents()
{
    var dict = new Dictionary<int,DisplayUnitDTO>();
    for(var i = 0; i < 5; i++)
    {
        dict.Add(i, new DisplayUnitDTO());
    }
    var repo = A.Fake<IDisplayUnitsRepository>();
    A.CallTo(() => repo.GetByEvent(VALID_GUID)).Returns(dict);
    A.CallTo(() => _fctry.ConvertFromDto(null))
        .WithAnyArguments()
        .Returns(
            new TextUnit(
                new Dictionary<string,string>(){
                    { "Text", "This is my Text" }
                }
            )
        );
    A.CallTo (() => _container.Resolve<IDisplayUnitsRepository>()).Returns(repo);
    var mgr = new DisplayUnitsRepoManager(_container);
    var duDict = mgr.GetByEvent(VALID_GUID);
    Assert.That(duDict, Is.InstanceOf(typeof(Dictionary<int,DisplayUnit>)));
    Assert.That(duDict, Is.Not.Null);
    Assert.That(duDict.Count == 5);
    foreach(var key in duDict.Keys)
    {
        Assert.That(duDict[key].PositionInEvent == key);
    }
}

So the comments were instructive here. 因此,这里的评论很有启发性。 Based upon those, I realized the direction I needed to look. 基于这些,我意识到我需要寻找的方向。 The culprit here was the line: 罪魁祸首是这条线:

 A.CallTo(() => _fctry.ConvertFromDto(null))
    .WithAnyArguments()
    .Returns(
        new TextUnit(
            new Dictionary<string,string>(){
                { "Text", "This is my Text" }
            }
        )
    );

Essentially, this had to do with FakeItEasy and the way it fakes return values. 本质上,这与FakeItEasy及其伪造返回值的方式有关。 Even though I had newed up a TextUnit in the return value, FakeItEasy took that new object and returned a reference to it ever single time that _fctry.ConvertFromDto() was called. 即使我在返回值中更新了一个TextUnit,FakeItEasy还是使用了该新对象,并在每次调用_fctry.ConvertFromDto()时返回了对该对象的引用。 Thus my fake was giving me strange behavior that otherwise wouldn't have been happening (I wouldn't ever add the same item multiple times to a dictionary by reference). 因此,我的假货给了我奇怪的行为,否则这种行为就不会发生(我永远不会通过引用将同一项目多次添加到字典中)。

Anyway, I was able to fix this by changing my return specification: 无论如何,我可以通过更改返回规格来解决此问题:

A.CallTo (() => _fctry.ConvertFromDto (null))
    .WithAnyArguments()
    .ReturnsLazily((DisplayUnitDTO d) => new TextUnit(d.Attributes));

After I tested this out, this creates a new text unit every time the function is called. 在测试完之后,每次调用该函数都会创建一个新的文本单元。 (btw... I know I didn't actually use d in the lambda, but I needed to fake the return value using the same signature the call would be made with.) (顺便说一句...我知道我实际上没有在lambda中使用d ,但是我需要使用与调用相同的签名来伪造返回值。)

Thanks for commenters and their pointers. 感谢评论者及其指点。 I've renamed this question to better relate to what was actually happening. 我已将这个问题重命名为更好地与实际发生的事情相关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 c#Linq XML-为什么将空白添加到带引号/命名空间的元素中? - c# Linq XML - Why Is Whitespace Being Added to Elements With Quotes / Namespace? C#字典添加后找不到键 - C# Dictionary doesn't find the key after being added 当我将元素添加到字典时,这些元素也将添加到另一个字典(C#+ XNA) - When I add elements to a dictionary, the elements are also added to another dictionary (C# + XNA) 项目已添加。 字典中的键:“主机名”被添加的键:c#中的“主机名” - Item has already been added. Key in dictionary: 'Hostname' Key being added: 'Hostname' in c# C#字典值引用类型 - 请解释为什么会发生这种情况 - C# dictionary value reference type - please explain why this happens 为什么我的INT变量通过引用传递? C# - Why is my INT variable being passed by reference?? C# C#字典是仅存储对象的引用还是作为键添加的对象的完整副本? - Does C# dictionary store only the reference of the object or the full copy of the object which is added as key? 访问C#词典中的元素 - Accessing elements in C# dictionary C#防止在类外部设置字典属性元素? - C# prevent dictionary property elements from being set outside class? 无法添加对“…dll”的引用。 C# - - A reference to the “…dll” could not be added. c# -
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM