I have an issue with trying to modify a list of Transactions within a foreach. I have created copies of the list passed into my method, made it read only, and yet when I try to change a value within any of the lists it changes that value within them all. Some type of memory link? I am unsure how to resolve this issue.
My program starts by declaring a class called Transaction (which is a generic class having Name, Value, Formatting), then I have subClasses :Transaction. I create a TransList (from public class TransList : IEnumerable) which has an object instance of each subClass. So a TransList would include a class named TranID, Amount, OrderID, Time, CardType, Comment1, Comment2. Each value of these subclasses could be string, decimal, DateTime. After that a list of TransParts is created it is then put into a larger list called processTrans.
So Comment2 is the element with a payment citation number and if there is more than one number in there I want to separate that into more than one TransList add those new TransLists to processTrans and remove the non-separated one. From my code below, having tried every strategy, run-time modification occurs to not only the intended processTrans but also to tempProcessTrans, addOn, tran, tranPart.
If processTrans that is passed into method looks like this in the debugger locals
processTrans [0] _Items TranID.Value = SD234DF and Comment2 = adf;wer;
Then the output should be
processTrans [0] _Items TranID.Value = SD234DF-1 and Comment2.Value=adf
processTrans [1] _Items TranID.Value = SD234DF-2 and Comment2.Value=wer
I currently get
processTrans [0] _Items TranID.Value = SD234DF-1-2 and Comment2.Value=wer
processTrans [1] _Items TranID.Value = SD234DF-1-2 and 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
{}
The behavior you're seeing is an inherent feature of reference types. When you call the ChangeTrans() method, the reference returned by that method is exactly the same as the one you passed in, which is that original value tran. Within the inner loop, the value of tran never changes, so on each iteration of the loop, you are modifying the same object over and over, adding it to your addOn list with each iteration.
This has two undesirable effects:
There is no difference between each element in the addOn list. They are all identical, referencing the same single object.
Any modification of any single element in the addOn list, or via the original reference to that single object, is visible via every other reference to that same single object. I.e. via all of the other elements in the list, and even that original reference in the tran variable (and of course, the copyTranList variable, which was assigned to the value of tran).
Without a more complete code example, it's not possible to know for sure what the best solution would be. However, one naïve solution would be to simply change your ChangeTrans() method so that it is responsible for making the new copy:
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;
}
Note: I have no idea if the above actually would compile, or if it actually copies all of the values needed. I reiterate: since you have not shown the TransList or Transaction data structures, it's not possible to know what all in them needs to be copied, nor what the best way to copy those values would be.
That said, note in the above example that this version of the method:
Creates an entirely new instance of the TransList object, storing the reference in newTransList.
For each Transaction value to be modified, it creates an entirely new instance of Transaction (using the appropriate type), assigning to that instance's Value property the modified value.
For each of those new Transaction objects, it adds the object to the newly-created TransList object referenced by the newTransList variable.
Finally, it returns that newly-created TransList object, rather than the one that was passed to the method.
Presumably you know what the correct way to add Transaction elements to TransList object, as well as whether there are other members in a Transaction object that would need to be copied. The above is simply a basic illustration of where and how you can modify your code so that you do the "deep copy" needed to avoid the problem you're describing.
Related
I am looping through a List, and trying to instantiate one of the properties as a string, but it returns the type:
{Namespace.Collection}
If I put a break-point, I can see that it holds the value I need.
How can I make it return the value and not the type?
foreach (var PropertyName in ListName) {
string n = PropertyName.ToString();
}
UPDATE (added more of my code, as well as an attempt of implementing suggested solutions):
foreach (DataRow dr in ds.Tables[0].Rows) {
//PaidTrips is my ObservableCollection instance.
//PaidTrip is my holder class which it has been bound to.
PaidTrips.Add(new PaidTrip {
LicenseHolderID = dr[0].ToString(),
// adding more properties
});
List<PaidTrip> theseTrips = PaidTrips
.GroupBy(p => new { p.LicenseHolderID })
.Select(g => g.First())
.ToList();
foreach (PaidTrip PaidTrips in theseTrips) {
foreach (var LicenseHolderID in PaidTrips.GetType().GetProperties()) {
string n = LicenseHolderID.GetValue(PaidTrips).ToString();
// code to create PDF
}
gfx.DrawString(n, new XFont("Arial", 40, XFontStyle.Bold), ridelGreen, new XPoint(40, 350));
This is what I do with string n. But when the PDF is created, the string output is System.Action1[System.Action]`
What am I doing wrong?
You need to loop through the Property Types in your custom class, after looping through the list. First we need an additional loop - to loop through each ClassName Object in ListName list.
foreach (ClassName myObj in ListName)
{
foreach (var PropertyName in myObj.GetType().GetProperties())
{
string n = PropertyName.GetValue(myObj).ToString();
}
}
Then we need to loop the actual properties of the current loop ClassName object.
Then you pass the argument .GetValue (as you are now looping through the properties - the actual properties assigned, not the definition of properties).
After, you still need to specify what object you want the value of. So by passing myObj, you are specifying the ClassName->Property of the current loop of ListName.
EDIT:
List<Notes> myNotesNow = new List<Notes>();
myNotesNow.Add(new Notes
{
note1 = "Valuye"
// adding more properties
});
List<Notes> theseTrips = myNotesNow;
foreach (Notes PaidTrips in theseTrips)
{
foreach (var myVariable in PaidTrips.GetType().GetProperties())
{
string n = myVariable.GetValue(PaidTrips).ToString();
string forBreakPoint = "";
// code to create PDF
}
}
For your question - I guess that ListName is not of type string and so you get the expected behavior (see: https://learn.microsoft.com/en-us/dotnet/api/system.object.tostring?view=net-5.0#the-default-objecttostring-method)
In that case you can override the ToString() function of that object to return whatever you need like this:
public override string ToString()
{
return "whatever you want including class properties";
}
On another note, the general approach to variable naming in C# is camelCase and starts with lower case so I suggest to name your variables propertName instead of PropertyName and listName instead of ListName.
Moreover - naming variables for how they are implemented (ListName) is not a best practice as it binds them together, not allowing flexibility in case implementation changes (that comment is true only if it makes sense, as I dont see all the code)
Cheers
I have a two class properdata and pprosecnddata both classes having property
I want to access product property from properdata class list object. How is it possible,below is my sample code
pupilc class ProperData
{
public string code{get;set;}
public List<ProSecndData>Secnd{get;set;}
}
public class ProSecndData
{
public string product{get;set;}
}
I am trying to call property like that
class Program
{
static void Main(string[] args)
{
ProperData.Secnd.Product = "Hello";
}
}
you cannot directly access property of Secnd as it is a list
you need to iterate or select the index of the List<Secnd>
you must initialize Secnd first and Secnd should have items in the list
properData.Secnd = new List<ProSecndData>();
so it can be access via
foreach(var second in properData.Secnd)
{
second.product = "hello";
}
//or
for(var i = 0; i < proderData.Secnd.Count(); i++)
{
properData.Secnd[i].product = "hello";
}
//or
var index = //0-length of list;
properData.Secnd[index].product = "hello";
if you want to have items first then add first on your Secnd List
properData.Secnd = new List<ProSecndData>();
properData.Secnd.Add(new ProSecndData{ product = "hello"});
then you now can iterate the list by using methods above
You are trying to access list as a single object, which is not possible.
you need to create single instance of your list class and then you can add string in that single instance.
properData.Secnd = new List<ProSecndData>();
ProSecndData proSecndData = new ProSecndData();
proSecndData.product = "Hello";
properData.Secnd.Add(proSecndData);
Actually I know the answer already, you have not created a constructor to initialise your List.
I'm guessing you get a object null ref error?
Create the constructor to initialise your list and it should be fine.
But in future, please post the error message (not the whole stack, just the actual error) as well as all the code required to repeat the issue. Otherwise you run the risk of getting your question deleted
(It should be deleted anyway because it could be considered a "what is a null ref err?" question).
Also you are accessing an item in a list like the list is that item (should be more like: ProperData.Secnd.elementAt(0).product, please also note the capitalisation of 'product' in the model vs your code.
I have a class that is instantiated at the beginning of each iteration of a loop. Inside the loop, it's attributes need to be populated with the row values of a table returned by a stored procedure. As I have to iterate through each column of every row, in order to know which attribute of the class needs to be assigned a value and when, I have a dictionary that maps the column names to an index. This index refers to a position in a list that stores an attribute of an instance of the class:
while (reader.Read() && reader.HasRows)
{
Subscription subscription = new Subscription();
List<string> subscrData = new List<string>
{
subscription.attr1,
subscription.attr2,
subscription.attr3,
subscription.attr4
}
Dictionary<string, int> columnDict = new Dictionary<string, int>
{
{"attr1": 0},
{"attr2":1},
{"attr3":2},
{"attr4":3}
}
foreach (string colName in columnDict.Keys)
{
if (reader.GetSchemaTable().Columns[colName] == null)
subscrData[columnDict[colName]] = "null";
else
{
subscrData[columnDict[colName]] = reader[colName].ToString();
nullsReturned = false;
}
}
I'm probably coming at this from more of a C++ approach as with that you could store references to the class instance an modify its attributes, but this doesn't work with C# because lists store the values.
How can I restructure this code so that I can modify the actual attributes of the class instance while still being able to check if each column returned from the stored procedure is not null?
You don't need the list for this case. You either want to add a method like setAttribute(string attributeName) to your class (and within it build a switch/case to modify the given attribute); or, use reflection to change an instance field given its name.
I agree with Hasan. But just for your information: to implement your approach you could make use of Lambda expressions to keep track of the references to your properties (= the attributes).
Something like this would work:
Subscription subscription = new Subscription();
List<Expression<Func<Subscription, string>>> subscrData = new List<Expression<Func<Subscription, string>>>
{
a => a.attr1,
a => a.attr2,
a => a.attr3,
a => a.attr4,
};
//E.g. To update attribute 3 you can do this:
var prop = (PropertyInfo)((MemberExpression)subscrData[2].Body).Member;
prop.SetValue(subscription, "test string", null);
I have a class which have a list
public static List<bar> tempList = new List<bar>();
public static Foo foo = new Foo();
public class bar(){
public string name;
public int age;
}
public class Foo(){
public List<bar> lBar = new List<bar>();
}
I have several textbox controls: age1, age2
on textChange on each control a create a new object
/*------------------------------------------------------------------
Following code: I want runtime calculation for a logic i did with age.
also need to create a new object using the inputs
------------------------------------------------------------------*/
age1_textChaned(...){
createObj( );
}
age2_textChaned(...){
createObj( );
}
private void createObj(){
if(tempList.Count != 0)
tempList.Clear();
if(age1.Text != "")
tempList.Add(new bar("name1", Convert.ToInt32(age1.text));
if (age2.Text != "")
tempList.Add(new bar("name2", Convert.ToInt32(age2.text));
}
Then i have a button btn1 which will create the object then clear the content of textbox.
btn1_Click(...){
foo.lBar = tempList;
clearFields(); //here lies the question, once i clear the fields,
//somehow it is still affecting the values in foo.lBar;
}
private void clearFields(){
age1.Text = "";
age2.Text = "";
}
so when i do this
btn2_Click(...){
foreach(bar b in foo.lBar){ //foo.lBar is empty i dont know why
...
}
}
my current solution on btn1_click i have this
foreach(bar b in tempList)
foo.lBar.Add(b); // instead of foo.lBar = tempList
is the foo.lBar = templist causing these changes?
snippet is only a simpler version of an entirely different project.
Objects in C# are passed around by reference unless specified otherwise.
For example, here is the code you are running and how it works behind the scenes :
// create a new location in memory and refer to it using the variable tempList
public static List<bar> tempList = new List<bar>();
// add a new item to the list
tempList.Add(new bar("name1", Convert.ToInt32(age1.text));
// make the variable foo.lBar to also refer to the same spot in memory as tempList
foo.lBar = tempList;
// clear the spot in memory containing the list
tempList.Clear();
That last line affects both the tempList variable and the foo.lBar variable because they both refer to the same location in memory.
The solution to avoid this is to create a new copy of an object in memory so the two variables are pointing to two separate instances in memory, and clearing one does not clear the other.
That is why your current solution works
// add the memory location of each item in tempList to foo.lBar's list
foreach(bar b in tempList)
foo.lBar.Add(b);
Note that if you call tempList.Clear() it will only clear the memory references being stored in tempList, however the actual objects will still exist in memory elsewhere.
Also with this solution if you did something like this :
tempList[0].name = "A changed name";
it would change the name property of the item in the foo.lBar list as well, since they both share the same reference in memory.
I have the following code:
public void Start()
{
List<StructCustomer> listCustomer = new List<StructCustomer>();
listCustomer.Add(
new StructCustomer { ID = 0, name = "Any Default Name", birthday = DateTime.Now });
DoSomethingWithStructList(listCustomer);
StructCustomer customer = listCustomer.First();
Console.WriteLine("ID = {0}, Name = {1}", customer.ID, customer.name); // Writes ID = 0, Name = "Any Default Name"
}
public void DoSomethingWithStructList(List<StructCustomer> listStructs)
{
StructCustomer test = listStructs.First();
test.ID = 2;
test.name = "Edited by method";
Console.WriteLine("ID = {0}, Name = {1}", test.ID, test.name); // Writes "ID = 2, Name = Edited by method"
}
public struct StructCustomer
{
public int ID { get; set; }
public string name { get; set; }
public DateTime birthday { get; set; }
}
As you can notice, the variable List is a reference to a List of Customer. Shouldnt the value be edited in the StructCustomer Variable in the List?
I know Structs are value and not reference types, but i am boxing it in a List!
Well, when you do this:
StructCustomer test = listStructs.First();
test.ID = 2;
test.name = "Edited by method";
Console.WriteLine("ID = {0}, Name = {1}", test.ID, test.name);
you are actualy creating a copy of the first struct in the listStructs, so, you'll change the values of the copy, not the real one. Try doing this instead - it should works:
listStructs.First().ID = 2;
listStructs.First().name = "Edited by method";
So, thats it ;)
OBS: This approach is not recomended by CPU usage, but, its a way out =)
Structs are value types, and as such, when you retrieve them from the list, as in your example, what you are retrieving is a copy of its value. Which you then modify. This does not change anything in the original that is contained in the list.
If you want to make changes to the element in the list, do it like this:
listStructs[0].ID = 2;
listStrings[0].name = "Edited by method";
Creating a list of a structure type will cause each item of the list to encapsulate all of the fields within the structure. The list's indexed 'get' method will copy all of the fields associated with a list item to the corresponding fields of the return value; the indexed 'put' will copy all of the fields from the passed-in item to the corresponding fields associated with the appropriate list item. Note that neither the 'get' nor the 'put' creates any attachment between the item in the list and the item which is read or written; future changes to one will not affect the other.
For many kinds of programs, this kind of detachment will sometimes be desirable and sometimes not. To help facilitate such cases, I would suggest creating a type like:
class SimpleHolder<T> { public T Value; /* A field, not a property! */ }
and then using a List<SimpleHolder<StructCustomer>>.
Your class should create every SimpleHolder<StructCustomer> instance itself, and never expose references to any of those instances to outside code. If you want to have a method, e.g. return item 0, use:
StructCustomer FirstCustomer()
{
return listStructHolders[0].Value;
}
To store a passed-in value:
void AddCustomer(StructCustomer newCustomer)
{
var temp = new SimpleHolder<StructCustomer>();
temp.Value = newCustomer;
listStructHolders.Add(temp);
}
To modify the name of customer 3:
listStructHolder[3].Value.name = "Fred";
Using a simple "exposed-field holder" class will make it easy to combine the advantages of structure types and mutable classes.
The List contains value types, so it returns a values when you ask it for an item. Try making a List and you'll see the same behavior.
As a result, you need to make assignments directly to the list item inside the list. You can have behavior semantically closer to what you are trying to do by iterating over the list in a foreach loop.