2016-05-15 6 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. Каждый из них. Зачем???? Как я могу делать то, что я пытаюсь сделать. Похоже, это должно быть просто.

Update:

Было предложено, чтобы показать, как я 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

Как вы создаете объект Display unit. Похоже, что все четыре элемента в словаре ссылаются на один и тот же экземпляр DusplayUnit. – ShuberFu

+0

Можете ли вы показать декларацию 'DisplayUnit' тоже, пожалуйста? Кстати: ключевое слово 'ref' кажется ненужным, так как вы только меняете содержимое этого словаря, а не саму ссылку, но это не должно быть ошибкой. –

+1

Я создал простой скрипт JS для этого: https://dotnetfiddle.net/T1Z9PW. Я согласен с Sterling 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)); 

После я проверил это, это создает новый текстовый блок каждый раз при вызове функции. (Кстати ... Я знаю, что я фактически не использовал d в лямбда, но мне нужно было подделать возвращаемое значение, используя ту же подпись, с которой будет выполнен вызов.)

Спасибо за комментарии и их указатели. Я переименовал этот вопрос, чтобы лучше относиться к тому, что на самом деле происходит.

+0

Суть дела в том, что «когда аргументы функции оцениваются?»: Перед вызовом функции. Первоначальный 'Returns (новый TextUnit (...))' должен сначала создать TextUnit, поэтому может использоваться только одно значение. Это то же самое, что если бы вы написали 'var theTextUnit = новый TextUnit (...); A.CallTo (...) .Returns (theTextUnit) '. В этом случае вы не были бы удивлены, если бы каждый раз использовался один и тот же «TextUnit». –

 Смежные вопросы

  • Нет связанных вопросов^_^