2016-05-15 64 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」,您都不會感到驚訝。 –

相關問題