2015-01-08 4 views
0

У меня проблема с попыткой изменить список транзакций в пределах foreach. Я создал копии списка, переданного в мой метод, сделал его только для чтения, и все же, когда я пытаюсь изменить значение в любом из списков, оно меняет это значение внутри них. Какой-то тип памяти? Я не уверен, как решить эту проблему.Нежелательная модификация списка

Моя программа начинается с объявления класса Transaction (который является общим классом с именем, значением, форматированием), тогда у меня есть subClasses: Transaction. Я создаю TransList (из открытого класса TransList: IEnumerable), который имеет экземпляр объекта для каждого подкласса. Таким образом, TransList будет включать класс с именем TranID, Amount, OrderID, Time, CardType, Comment1, Comment2. Каждое значение этих подклассов может быть строковым, десятичным, DateTime. После этого создается список TransParts, который затем помещается в более крупный список, называемый processTrans.

So Comment2 - это элемент с номером ссылки на оплату, и если там есть более одного номера, я хочу разделить это на несколько TransList, чтобы добавить эти новые TransLists в processTrans и удалить неразделенный. Из моего кода ниже, пробовав каждую стратегию, изменение времени выполнения происходит не только с предполагаемыми processTrans, но и с tempProcessTrans, addOn, tran, tranPart.

Если processTrans, который передается в метод выглядит в отладчике местного
processTrans [0] _Items TranID.Value = SD234DF и Comment2 = ADF; WER;
Тогда выходной сигнал должен быть
processTrans [0] _Items TranID.Value = SD234DF-1 и Comment2.Value = ADF
processTrans [1] _Items TranID.Value = SD234DF-2 и Comment2.Value = Wer
I В настоящее время получить
processTrans [0] _Items TranID.Value = SD234DF-1-2 и Comment2.Value = Wer
processTrans [1] _Items TranID.Value = SD234DF-1-2 и Comment2.Value = Wer

public static List<TransList> SeperateMultiCitations(List<TransList> processTrans) //change TransList seperating Multiple Citations 
    { 
     List<int> indexes=new List<int>(); 
     IList<TransList> tempProcessTrans = processTrans.AsReadOnly(); //this didn't help 
     List<TransList> addOn= new List<TransList>(); //copy list didn't stop from changes to occur in processTrans at same time 
     foreach (TransList tran in tempProcessTrans.ToList()) 
     { 
      TransList copyTransList = tran; 
      foreach (Transaction tranPart in tran.OfType<Comment2>()) 
      { 
       if (new Regex(";.+;").IsMatch((string)tranPart.Value, 0)) 
       { 
        string[] citations = Regex.Split((string)tranPart.Value, ";").Where(s => s != String.Empty).ToArray(); 
        int citNumb = 1; 
        indexes.Add(tempProcessTrans.IndexOf(tran)); 

        foreach (string singleCitation in citations) 
        { 
         addOn.Add(ChangeTrans(tran, singleCitation, citNumb++)); when this line runs changes occur to all lists as well as trans, tranPart 
        } 
        break; 
       } 
      } 
     } 
     foreach (int index in indexes.OrderByDescending(x => x)) 
     { 
      processTrans.RemoveAt(index); 
     } 
     processTrans.AddRange(addOn); 
     return processTrans; 
    } 
public static TransList ChangeTrans(TransList copyTransList, string singleCitation, int citNumb) //add ConFee 
    { 
     foreach (Transaction temp in copyTransList.OfType<TranID>()) 
     { 
      temp.Value += "-" + citNumb; 
     } 
     foreach(Transaction temp in copyTransList.OfType<Comment2>()) 
     { 
      temp.Value = singleCitation; 
     } 
     foreach (Transaction temp in copyTransList.OfType<Amount>()) 
     { 
      //temp.Value = DboGrab(temp); 
      //temp.Value = amount; 
     } 

     return copyTransList; 
    } 

public class Transaction : TranInterface 
{ 
    public string Name; 
    public object Value; 
    public string Formating; 

    public Transaction(string name, object value, string formating) 
    { 
     Name = name; 
     Value = value; 
     Formating = formating; 
    } 
} 

class TranID : Transaction 
     { 
      public TranID(string Name, string Value, string Formating) : base("Transaction ID", Value, "@") { } 
     } 
public class TransList : IEnumerable<Transaction> //not to add all the lengthy parts here but this just allows for adding the parts and iterating through them in the foreach statements 
{} 
+0

Можете ли вы показать код, который создает эти объекты и добавить их в исходный список? –

+0

Вы не создаете новые экземпляры своих объектов, поэтому ваши изменения в ChangeTrans происходят в исходном классе. Если я правильно понял вашу проблему, при обработке каждого элемента он будет преобразован в один или несколько элементов на основе результатов регулярного выражения? Если вы реструктурируете вопрос с дополнительной информацией о своей структуре, у вас могут быть лучшие ответы. –

+1

TransList copyTransList = tran; не копирует 'tran'. Это только создает другую ссылку на tran. Вам нужна глубокая копия. Обычно это делается путем создания конструктора, который возьмет объект своего типа, а затем скопирует все значения. – MrFox

ответ

3

Поведение, которое вы видите, является неотъемлемой чертой reference types. Когда вы вызываете метод ChangeTrans(), ссылка, возвращаемая этим методом, точно такая же, как и та, которую вы передали, и это исходное значение tran. Внутри внутреннего цикла значение tran никогда не изменяется, поэтому на каждой итерации цикла вы снова и снова изменяете один и тот же объект, добавляя его в список addOn с каждой итерацией.

Это имеет два нежелательных эффектов:

  1. Там нет никакой разницы между каждым элементом в списке addOn. Все они идентичны, ссылаясь на один и тот же объект.
  2. Любая модификация любого отдельного элемента в списке addOn или через исходную ссылку на этот единственный объект видна через любую другую ссылку на тот же единственный объект. То есть через все остальные элементы в списке и даже эту исходную ссылку в переменной tran (и, конечно, переменную copyTranList, которая была присвоена значению tran).

Без более полного примера кода невозможно точно знать, какое наилучшее решение будет.Тем не менее, один наивный решение было бы просто изменить метод ChangeTrans() так, что он несет ответственность за новую копию:

public static TransList ChangeTrans(
    TransList copyTransList, string singleCitation, int citNumb) //add ConFee 
{ 
    TransList newTransList = new TransList(); 

    foreach (Transaction temp in copyTransList.OfType<TranID>()) 
    { 
     Transaction newTransaction = new TranID(); 

     newTransaction.Value = temp.Value + "-" + citNumb; 
     newTransList.Add(newTransaction); 
    } 
    foreach(Transaction temp in copyTransList.OfType<Comment2>()) 
    { 
     Transaction newTransaction = new Comment2(); 

     newTransaction.Value = singleCitation; 
     newTransList.Add(newTransaction); 
    } 

    return newTransList; 
} 

Примечание: Я понятия не имею, если выше на самом деле будет составлять, или, если он действительно копирует все необходимых значений. Я повторяю: поскольку вы не показывали структуры данных или Transaction, невозможно знать, что все в них нужно скопировать, и что лучший способ скопировать эти значения.

Тем не менее, обратите внимание, в приведенном выше примере, что эта версия метода:

  1. создает совершенно новый экземпляр объекта TransList, хранение ссылки в newTransList.
  2. Для каждого значения Transaction быть изменен, это создает совершенно новый экземпляр Transaction (с использованием соответствующего типа), сопоставляя этого экземпляра Value собственности измененное значение.
  3. Для каждого из этих новых объектов Transaction он добавляет объект к вновь созданному объекту TransList, на который ссылается переменная newTransList.
  4. И наконец, он возвращает вновь созданный объект TransList, а не тот, который был передан методу.

Предположительно вы знаете, что правильный способ добавить Transaction элементов для TransList объекта, а также есть ли другие члены в Transaction объекте, который необходимо будет скопировано. Вышесказанное является просто базовой иллюстрацией того, где и как вы можете изменить свой код, чтобы вы делали «глубокую копию», чтобы избежать описанной проблемы.

+0

Я понимаю ваш ответ, мой вопрос к этому, хотя, может быть, у меня не будет двух разных копий, например, TranID в TransList? И поэтому сначала нужно удалить старый. – Edward

+0

Или перечитывать, я думаю, вы просто заставляете меня создавать новый TransList в этом методе, а затем передавать его обратно? Конечно, чтобы быть правдой, мне нужно, чтобы каждый подкласс Transaction был создан и добавлен в новый TransList внутри. Верный? – Edward

+0

Вы понимаете пример кода, который я написал. К сожалению, ваш оригинальный код недостаточно для меня, чтобы действительно понять, что на самом деле нужно. Все, что я могу сказать, это то, что любой объект, который вы хотите изменить, не влияет на существующие экземпляры, вам нужно сделать копию объекта, прежде чем изменять его (или создать совершенно новый с нуля). Вы добавляли объект TransList к совершенно новому списку, что может или не означает, что вам нужен совершенно новый объект TransList. Обратите внимание, что если вы не создадите новый список, это означает, что новые, измененные объекты будут добавлены в ваш исходный 'TransList'. –