2016-05-15 8 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; 
    } 
} 

私は(それは常にこのようなシーケンシャルではありませんが、私はユニットそれをテストだ)0-4のキーを持つ5つのオブジェクトの辞書でこれを実行すると、PositionInEventイベントの各項目のプロパティは4の値を持ちます。なぜ????私は何をしようとしているのですか?それは簡単なように思える。

更新:

それは、私がDisplayUnitが宣言されている方法を示すことが要求されたインスタンス化し、辞書に追加されました。 TextUnit私は、使用して実際にだクラスです

/// <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; 
     } 
    } 

} 

はここでクラス宣言(私はインスタンス化とは無関係のものと私はここで働いているプロパティを取り出しました)です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

どのように表示単位オブジェクトを作成していますか。辞書の4つの項目すべてが同じDusplayUnitインスタンスを参照しているようです。 – ShuberFu

+0

'DisplayUnit'の宣言もあまりにも表示できますか? btw: 'ref'キーワードは、参照自体ではなくその辞書の内容を変更するだけなので不要です。しかし、それはエラーであってはなりません。 –

+1

私はこのためにシンプルなJS Fiddleを作成しました: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)); 

私はこれをテストした後、これは新しいテキスト部関数が呼び出されるたびに作成されます。 (btw ...ラムダで実際にdを使用していないことは分かっていますが、同じ署名を使用して戻り値を偽装する必要がありました)

コメント欄とそのポインタをありがとう。私はこの問題を、実際に起こっていたことにもっと関連させるために改名しました。

+0

問題の要点は、「関数の引数がいつ評価されるのか」です。関数が呼び出される前です。元の 'Returns(new TextUnit(...))'は 'TextUnit'を最初に作成しなければならないので、使用できる値は1つだけです。 'var theTextUnit = new TextUnit(...);と書いたのと同じです。 A.CallTo(...).Returns(theTextUnit); '。その場合、毎回同じ 'TextUnit'が使用された場合、あなたは驚いたことはありません。 –

関連する問題