2016-05-15 110 views
1

我试图做一些看起来应该很容易的事情,但它不起作用。我有一个int键的字典对象。在对象中,我有一个属性PositionInEvent,我想匹配它中的字典的键。看起来这应该是一个简单的循环操作,但它不起作用。下面是我有:为什么在C#中通过引用添加字典元素?

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

当我在5个对象与0-4键的字典运行这个(它不会永远是连续的这样的,但我的单元测试的话),在PositionInEvent事件中每个项目的属性值为4.每一个。为什么????我该怎么做我想做的事情。它看起来应该很简单。

更新:

有人要求我展示DisplayUnit如何声明,实例化,并添加到字典中。

下面是类声明(我已经采取了的东西无关的实例,我在这里工作的属性):

/// <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是类我使用,我实际上这源自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 
} 

正在使用的字典来自此处。 _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); 
    } 
} 

这一切都来源于此单元测试(我不能得到通过,我不知道为什么):

[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); 
    } 
} 
+4

你是如何创建显示单元对象的。它看起来像字典中的所有四个项目都引用相同的DusplayUnit实例。 – ShuberFu

+0

您是否也可以显示'DisplayUnit'的声明?顺便说一句:'ref'关键词似乎没有必要,因为你只是改变该字典的内容,而不是引用本身,但这不应该是错误。 –

+1

我为此创建了一个简单的JS小提琴:https://dotnetfiddle.net/T1Z9PW。我同意斯特林W说你的字典的构建是可疑的。 – syazdani

回答

2

所以评论是有益的在这里。基于这些,我意识到我需要寻找的方向。这里的罪魁祸首是行:

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

从本质上讲,这个曾与FakeItEasy而且假货返回值的方式做。尽管我已经在返回值中创建了一个TextUnit,但FakeItEasy接受了这个新对象,并且一次返回一个引用_fctry.ConvertFromDto()被调用。因此,我的假给了我奇怪的行为,否则就不会发生(我不会通过引用多次将相同的项目添加到字典中)。

反正我是能够改变我的回报规范解决这个问题:

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

我测试了这一点之后,这将创建一个新的文本单元在每次函数调用时。 (顺便说一句...我知道我实际上并没有在lambda中使用d,但我需要使用相同的签名来调用返回值。)

感谢评论者和他们的指针。我已经将这个问题重新命名为更好地涉及到实际发生的事情。

+0

问题的关键是“函数参数何时被评估?”:在函数被调用之前。最初的'Returns(new TextUnit(...))'必须首先创建'TextUnit',所以只能使用一个值。这和你写'var theTextUnit = new TextUnit(...); A.CallTo(...).Returns(theTextUnit);'。在这种情况下,如果每次使用相同的“TextUnit”,您都不会感到惊讶。 –

相关问题