Information Storage for 3 variables - c#

I am trying to create a good way to store 3 variables, two ints and a point in C# programming.
I thought of a way by using an array of dictionaries
Dictionary<int, Point>[] ItemList = new Dictionary<int, Point>[4];
The Idea was that one variable has to be between 1 and 4, so I would have that as the sorting point, or each array location. The 2nd int, has to be between 0 and 15, and the point is on a 4x4 grid. I thought this method would work, and it would have except that You can't have the same key in a dictionary, and since both ints will be repeated, I can't swap them out. This idea also went out the window, same problem
Dictionary<int, int>[,] ItemList = new Dictionary<int, int>[4,4];
I also thought of using a tuple, and I don't have much(any) experience with it, and my experiments with them weren't going so well. The problem with it was I couldn't get the count of how many items were in it. I set one up like this.
Tuple<int, Point>[] ItemList = new Tuple<int, Point>[4];
Same Idea as my first example, Its just there is no code like this
ItemList[1].Count /*OR*/ ItemList[1].Length
Please let me know if I am missing something terribly obvious with tuples, or suggest an different storage method that would be nice to have all 3 variables stored all together.

You can use the Tuple to store directly the 3 data structures. A Tuple can have more than two items, and of any type. That way, you don't have to use your array:
Tuple<int, int, Point>
To get the values, use the corresponding Item property. For the first int, it will be yourTuple.Item1. For the second one yourTuple.Item2 and for the Point yourTuple.Item3.
If you have multiple Tuples, you can use a classic List to store them all:
var tuples = new List<Tuple<int, int, Point>>();
Since it's a list, you can get the count easily: tuples.Count()

So a class seems like the proper structure to me.
public class Something {
public int Item1 { get; set; }
public int Item2 { get; set; }
public Point Location { get; set; }
}
Then you store these objects in a List<>
var List<Something> list = new List<Something>();
add items to the list...
list.Add(new Something() {Item1 = 4, Item2 = 8, Point = new Point(x,y)});
then use some LINQ to get just the ones you want.
var onlyItem1IsFour = (from item in list where 4 == item.Item1 select item).ToList();
excuse my LINQ. I'm used to VB and may have gotten the casing/syntax slightly wrong

Well, Using the idea of using a list, I solved my problem. Its kinda a hybrid between the suggested ideas and my original idea with using the array. You don't have to use an array if you are looking to do something similar, you can use a tuple with 3 values, I just needed an array for one int value because I needed them stored separately, based off of what that one int value was (between 0 and 4). Here is some code that would work.
List<Tuple<int, Point>>[] ItemList = new List<Tuple<int, Point>>[4]; // how to declare it
for (int i = 0; i < 4; i++)
{
ItemList[i] = new List<Tuple<int, Point>>(); // initilize each list
}
ItemList[1].Add(new Tuple<int, Point>(5, new Point(1, 2))); // add a new tuple to a specific array level
int count = ItemList[1].Count; // finds the count for a specific level of the array --> (1)
int getInt = ItemList[1].ElementAt(0).Item1; // finds int value --> (5)
Point getPoint = ItemList[1].ElementAt(0).Item2; // finds the point --> (1,2)

Related

List of arrays holding 2 objects each

What is the correct way to define a List holding arrays of objects of type T?
Each array holds only 2 objects.
Based on this definition:
working: double[] balance = new double[10];
I tried
not working: List<T[]> arrList = new List<T[2]>();
I need to build a list that looks like
([obj1,obj2] ... [objn,objm] ...)
Thank you
D.
There is no way to use a fixed-size for the arrays in the list. You have to do this:
List<T[]> arrList = new List<T[]>();
and then trust the outside code that each array added to the list will have exactly two items.
The only other options I can think of right now are using tuples:
List< (T,T) > arrList = new List< (T,T) >();
or creating your own class type for the List with an indexer property and exactly two members.

c# a collection to hold two arraylist

I am using C#.
I've created 2 arraylists called Names and Performances and the data in them are filled from my database.
I wanna create an arraylist or something like an array of 2 dimensions so
array[Name][Performance].
array[0][0] => should give me first persons first performance data
array[0][1] => should give me firstpersons second performance data
array[0][2] => should give me first persons third performance data...
The size of Names and Performance are changeable due to diffrent sql queries.
since sizes are changeable i tried to use loops and size of Name and Performance arraylists in those loops. How can i handle that collection to hold both of my arraylists?
Why don't you use a generic List ?
You can then for instance create a type which holds the Person and his performance data, like this:
class PersonPerformance
{
public string Name { get; set; }
public List<Performance> Performances { get; set; }
}
And you create a collection easily:
var performances = new List<PersonPerformance>();
You can use something like that
IList<KeyValuePair<string, string>> innerList = new List<KeyValuePair<string, string>>();
IList<IList<KeyValuePair<String,string>>> list=new List<IList<KeyValuePair<string, string>>>();
list.Add(innerList);
Where KeyValuePair contains Name and Perfomance element
I assume your Names are of the type string.
I usually do (if I don't have set size)
List<KeyValuePair<string, TYPE_X>> list = new List<KeyValuePair<string, TYPE_X>>();
or (the size is known)
KeyValuePair<string, TYPE_X>[] array = new KeyValuePair<string, TYPE_X>[size];
or better
public Dictionary<string, TYPE_X> dict = new Dictionary<string, TYPE>();
if names are unique and I want to acces them like that (not in order):
TYPE_X object = dict["myName"];
You can use Use Dictionary<Name,List<Performance>> Type
hence for each name you get the set of Performances

Store variables in list/array and loop through later c#

Sorry, I think I was not clear earlier. I am trying to do as O.R.mapper says below- create a list of arbitrary variables and then get their values later in foreach loop.
Moreover, all variables are of string type so I think can come in one list. Thanks.
Is there a way to store variables in a list or array then then loop through them later.
For example: I have three variables in a class c named x,y and Z.
can I do something like:
public List Max_One = new List {c.x,c.y,c.z}
and then later in the code
foreach (string var in Max_One)
{
if ((var < 0) | (var > 1 ))
{
// some code here
}
}
Is there a particular reason why you want to store the list of variables beforehand? If it is sufficient to reuse such a list whenever you need it, I would opt for creating a property that returns an IEnumerable<string>:
public IEnumerable<string> Max_One {
get {
yield return c.x;
yield return c.y;
yield return c.z;
}
}
The values returned in this enumerable would be retrieved only when the property getter is invoked. Hence, the resulting enumerable would always contain the current values of c.x, c.y and c.z.
You can then iterate over these values with a foreach loop as alluded to by yourself in your question.
This might not be practical if you need to gradually assemble the list of variables; in that case, you might have to work with reflection. If this is really required, please let me know; I can provide an example for that, but it will become more verbose and complex.
Yes, e.g. if they are all strings:
public List<string> Max_One = new List<string> {c.x,c.y,c.z};
This uses the collection initializer syntax.
It doesn't make sense to compare a string to an int, though. This is a valid example:
foreach (string var in Max_One)
{
if (string.IsNullOrEmpty(var))
{
// some code here
}
}
If your properties are numbers (int, for example) you can do this:
List<int> Max_One = new List<int> { c.x, c.y, c.Z };
and use your foreach like this
foreach(int myNum in Max_One) { ... } //you can't name an iterator 'var', it's a reserved word
Replace int in list declaration with the correct numeric type (double, decimal, etc.)
You could try using:
List<object> list = new List<object>
{
c.x,
c.y,
c.z
};
I will answer your question in reverse way
To start with , you cannot name your variable with "var" since it is reserved name. So what you can do for the foreach is
foreach (var x in Max_One)
{
if ((x< 0) || (x> 1 ))
{
// some code here
}
}
if you have .Net 3.0 and later framework, you can use "var" to define x as a member of Max_One list without worrying about the actual type of x. if you have older than the version 3.0 then you need to specify the datatype of x, and in this case your code is valid (still risky though)
The last point (which is the your first point)
public List Max_One = new List {c.x,c.y,c.z}
There are main thing you need to know , that is in order to store in a list , the members must be from the same datatype, so unless a , b , and c are from the same datatype you cannot store them in the same list EXCEPT if you defined the list to store elements of datatype "object".
If you used the "Object" method, you need to cast the elements into the original type such as:
var x = (int) Max_One[0];
You can read more about lists and other alternatives from this website
http://www.dotnetperls.com/collections
P.s. if this is a homework, then you should read more and learn more from video tutorials and books ;)

Practical example where Tuple can be used in .Net 4.0?

I have seen the Tuple introduced in .Net 4 but I am not able to imagine where it can be used. We can always make a Custom class or Struct.
That's the point - it is more convenient not to make a custom class or struct all the time. It is an improvement like Action or Func... you can make this types yourself, but it's convenient that they exist in the framework.
With tuples you could easily implement a two-dimensional dictionary (or n-dimensional for that matter). For example, you could use such a dictionary to implement a currency exchange mapping:
var forex = new Dictionary<Tuple<string, string>, decimal>();
forex.Add(Tuple.Create("USD", "EUR"), 0.74850m); // 1 USD = 0.74850 EUR
forex.Add(Tuple.Create("USD", "GBP"), 0.64128m);
forex.Add(Tuple.Create("EUR", "USD"), 1.33635m);
forex.Add(Tuple.Create("EUR", "GBP"), 0.85677m);
forex.Add(Tuple.Create("GBP", "USD"), 1.55938m);
forex.Add(Tuple.Create("GBP", "EUR"), 1.16717m);
forex.Add(Tuple.Create("USD", "USD"), 1.00000m);
forex.Add(Tuple.Create("EUR", "EUR"), 1.00000m);
forex.Add(Tuple.Create("GBP", "GBP"), 1.00000m);
decimal result;
result = 35.0m * forex[Tuple.Create("USD", "EUR")]; // USD 35.00 = EUR 26.20
result = 35.0m * forex[Tuple.Create("EUR", "GBP")]; // EUR 35.00 = GBP 29.99
result = 35.0m * forex[Tuple.Create("GBP", "USD")]; // GBP 35.00 = USD 54.58
There's an excellent article in MSDN magazine that talks about the belly-aching and design considerations that went into adding Tuple to the BCL. Choosing between a value type and a reference type is particularly interesting.
As the article makes clear, the driving force behind Tuple was so many groups inside of Microsoft having a use for it, the F# team up front. Although not mentioned, I reckon that the new "dynamic" keyword in C# (and VB.NET) had something to do with it as well, tuples are very common in dynamic languages.
It is otherwise not particularly superior to creating your own poco, at least you can give the members a better name.
UPDATE: due for a big revision in C# version 7, now getting a lot more syntax love. Preliminary announcement in this blog post.
Here's a small example - say you have a method that needs to lookup a user's handle and email address, given a user Id. You can always make a custom class that contains that data, or use a ref / out parameter for that data, or you can just return a Tuple and have a nice method signature without having to create a new POCO.
public static void Main(string[] args)
{
int userId = 0;
Tuple<string, string> userData = GetUserData(userId);
}
public static Tuple<string, string> GetUserData(int userId)
{
return new Tuple<string, string>("Hello", "World");
}
I used a tuple to solve Problem 11 of Project Euler:
class Grid
{
public static int[,] Cells = { { 08, 02, 22, // whole grid omitted
public static IEnumerable<Tuple<int, int, int, int>> ToList()
{
// code converts grid to enumeration every possible set of 4 per rules
// code omitted
}
}
Now I can solve the whole problem with:
class Program
{
static void Main(string[] args)
{
int product = Grid.ToList().Max(t => t.Item1 * t.Item2 * t.Item3 * t.Item4);
Console.WriteLine("Maximum product is {0}", product);
}
}
I could have used a custom type for this, but it would have looked exactly like Tuple.
C#'s tuple syntax is ridiculously bulky, so tuples are painful to declare. And it doesn't have pattern matching, so they're also painful to use.
But occasionally, you just want an ad-hoc grouping of objects without creating a class for it. For example, let's say I wanted to aggregate a list, but I wanted two values instead of one:
// sum and sum of squares at the same time
var x =
Enumerable.Range(1, 100)
.Aggregate((acc, x) => Tuple.Create(acc.Item1 + x, acc.Item2 + x * x));
Instead of combining a collection of values into a single result, let's expand a single result into a collection of values. The easiest way to write this function is:
static IEnumerable<T> Unfold<T, State>(State seed, Func<State, Tuple<T, State>> f)
{
Tuple<T, State> res;
while ((res = f(seed)) != null)
{
yield return res.Item1;
seed = res.Item2;
}
}
f converts some state into a tuple. We return the first value from the tuple and set our new state to the second value. This allows us to retain state throughout the computation.
You use it as such:
// return 0, 2, 3, 6, 8
var evens =
Unfold(0, state => state < 10 ? Tuple.Create(state, state + 2) : null)
.ToList();
// returns 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
var fibs =
Unfold(Tuple.Create(0, 1), state => Tuple.Create(state.Item1, Tuple.Create(state.Item2, state.Item1 + state.Item2)))
.Take(10).ToList();
evens is fairly straightforward, but fibs is a little more clever. Its state is actually a tuple which holds fib(n-2) and fib(n-1) respectively.
I don't like the abuse of them, since they produce code that doesn't explain itself, but they're awesome to implement on-the-fly compound keys, since they implement IStructuralEquatable and IStructuralComparable (to use both for lookup and ordering purposes).
And they combine all of their items' hashcodes, internally; for example, here is Tuple's GetHashCode (taken from ILSpy):
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
{
return Tuple.CombineHashCodes(comparer.GetHashCode(this.m_Item1), comparer.GetHashCode(this.m_Item2), comparer.GetHashCode(this.m_Item3));
}
Tuples are great for doing multiple async IO operations at a time and returning all the values together. Here is the examples of doing it with and without Tuple. Tuples can actually make your code clearer!
Without (nasty nesting!):
Task.Factory.StartNew(() => data.RetrieveServerNames())
.ContinueWith(antecedent1 =>
{
if (!antecedent1.IsFaulted)
{
ServerNames = KeepExistingFilter(ServerNames, antecedent1.Result);
Task.Factory.StartNew(() => data.RetrieveLogNames())
.ContinueWith(antecedent2 =>
{
if (antecedent2.IsFaulted)
{
LogNames = KeepExistingFilter(LogNames, antecedent2.Result);
Task.Factory.StartNew(() => data.RetrieveEntryTypes())
.ContinueWith(antecedent3 =>
{
if (!antecedent3.IsFaulted)
{
EntryTypes = KeepExistingFilter(EntryTypes, antecedent3.Result);
}
});
}
});
}
});
With Tuple
Task.Factory.StartNew(() =>
{
List<string> serverNames = data.RetrieveServerNames();
List<string> logNames = data.RetrieveLogNames();
List<string> entryTypes = data.RetrieveEntryTypes();
return Tuple.Create(serverNames, logNames, entryTypes);
}).ContinueWith(antecedent =>
{
if (!antecedent.IsFaulted)
{
ServerNames = KeepExistingFilter(ServerNames, antecedent.Result.Item1);
LogNames = KeepExistingFilter(LogNames, antecedent.Result.Item2);
EntryTypes = KeepExistingFilter(EntryTypes, antecedent.Result.Item3);
}
});
If you were using an anonymous function with an implied type anyway then you aren't making the code less clear by using the Tuple. Retuning a Tuple from a method? Use sparingly when code clarity is key, in my humble opinion. I know functional programming in C# is hard to resist, but we have to consider all of those old clunky "object oriented" C# programmers.
Tuples are heavily used in functional languages which can do more things with them, now F# is a 'official' .net language you may want to interoperate with it from C# and pass them between code written in two languages.
I tend to avoid Tuple for most scenarios since it hurts readability. However, Tuple is useful when you need to group unrelated data.
For example, suppose you have a list of cars and the cities in which they were purchased:
Mercedes, Seattle
Mustang, Denver
Mercedes, Seattle
Porsche, Seattle
Tesla, Seattle
Mercedes, Seattle
You want to aggregate the counts for each car per city:
Mercedes, Seattle [3]
Mustang, Denver [1]
Porsche, Seattle [1]
Tesla, Seattle [1]
To do this, you create a Dictionary. You have a few options:
Create a Dictionary<string, Dictionary<string, int>>.
Create a Dictionary<CarAndCity, int>.
Create a Dictionary<Tuple<string, string>, int>.
Readability is lost with the first option. It will require you to write a lot more code.
The second option works and is succinct, but car and city aren't really related and probably don't belong in a class together.
The third option is succinct and clean. It's a good use of Tuple.
A few examples off the top of my head:
An X and Y location (and Z if you like)
a Width and Height
Anything measured over time
For example you wouldn't want to include System.Drawing in a web application just to use Point/PointF and Size/SizeF.
You should be very careful with using Tuple and probably think twice before do this. From my previous experience I found out that using Tuple makes code very difficult to read and support in the future. A while ago, I had to fix some code where tuples were used almost everywhere. Instead of thinking about proper object models, they just used tuples. That was nightmare... sometimes I wanted to kill the guy who wrote the code...
Don't want to say that you shouldn't use Tuple and it's evil or something and I'm hundred percent sure there are some tasks where the Tuple is the best candidate to be used, but probably you should think again, do you REALLY need it?
The best use for Tuples I have found is when needing to return more than 1 type of object from a method, you know what object types and number they will be, and it is not a long list.
Other simple alternatives would be using an 'out' parameter
private string MyMethod(out object)
or making a Dictionary
Dictionary<objectType1, objectType2>
Using a Tuple however saves either creating the 'out' object or having to essentially look-up the entry in the dictionary;
Just found the solution of one of my issues in Tuple. It is like declaring a class in scope of a method, but with lazy declaration of its fields names. You operate with collections of tuples, its single instances and then create a collection of anonymous type with the required field names, basing on your tuple. This avoids you from creating the new class for this purpose.
The task is to write a JSON response from LINQ without any additional classes:
//I select some roles from my ORM my with subrequest and save results to Tuple list
var rolesWithUsers = (from role in roles
select new Tuple<string, int, int>(
role.RoleName,
role.RoleId,
usersInRoles.Where(ur => ur.RoleId == role.RoleId).Count()
));
//Then I add some new element required element to this collection
var tempResult = rolesWithUsers.ToList();
tempResult.Add(new Tuple<string, int, int>(
"Empty",
-1,
emptyRoleUsers.Count()
));
//And create a new anonimous class collection, based on my Tuple list
tempResult.Select(item => new
{
GroupName = item.Item1,
GroupId = item.Item2,
Count = item.Item3
});
//And return it in JSON
return new JavaScriptSerializer().Serialize(rolesWithUsers);
Of cause we could do this with declaring a new Class for my groups, but the idea to create such an anonimous collections without declaring of new classes.
Well in my case, I had to use a Tuple when I found out that we cannot use out parameter in an asynchronous method. Read about it here. I also needed a different return type. So I used a Tuple instead as my return type and marked the method as async.
Sample code below.
...
...
// calling code.
var userDetails = await GetUserDetails(userId);
Console.WriteLine("Username : {0}", userDetails.Item1);
Console.WriteLine("User Region Id : {0}", userDetails.Item2);
...
...
private async Tuple<string,int> GetUserDetails(int userId)
{
return new Tuple<string,int>("Amogh",105);
// Note that I can also use the existing helper method (Tuple.Create).
}
Read more about Tuple here.
Hope this helps.
Changing shapes of objects when you need to send them across wire or pass to different layer of application and multiple objects get merged into one:
Example:
var customerDetails = new Tuple<Customer, List<Address>>(mainCustomer, new List<Address> {mainCustomerAddress}).ToCustomerDetails();
ExtensionMethod:
public static CustomerDetails ToCustomerDetails(this Tuple<Website.Customer, List<Website.Address>> customerAndAddress)
{
var mainAddress = customerAndAddress.Item2 != null ? customerAndAddress.Item2.SingleOrDefault(o => o.Type == "Main") : null;
var customerDetails = new CustomerDetails
{
FirstName = customerAndAddress.Item1.Name,
LastName = customerAndAddress.Item1.Surname,
Title = customerAndAddress.Item1.Title,
Dob = customerAndAddress.Item1.Dob,
EmailAddress = customerAndAddress.Item1.Email,
Gender = customerAndAddress.Item1.Gender,
PrimaryPhoneNo = string.Format("{0}", customerAndAddress.Item1.Phone)
};
if (mainAddress != null)
{
customerDetails.AddressLine1 =
!string.IsNullOrWhiteSpace(mainAddress.HouseName)
? mainAddress.HouseName
: mainAddress.HouseNumber;
customerDetails.AddressLine2 =
!string.IsNullOrWhiteSpace(mainAddress.Street)
? mainAddress.Street
: null;
customerDetails.AddressLine3 =
!string.IsNullOrWhiteSpace(mainAddress.Town) ? mainAddress.Town : null;
customerDetails.AddressLine4 =
!string.IsNullOrWhiteSpace(mainAddress.County)
? mainAddress.County
: null;
customerDetails.PostCode = mainAddress.PostCode;
}
...
return customerDetails;
}
An out parameter is great when there are only a few values that need to be returned,
but when you start encountering 4, 5, 6, or more values that need to be returned, it
can get unwieldy. Another option for returning multiple values is to create and return
a user-defined class/structure or to use a Tuple to package up all the values that need
to be returned by a method.
The first option, using a class/structure to return the values, is straightforward. Just
create the type (in this example it is a structure) like so:
public struct Dimensions
{
public int Height;
public int Width;
public int Depth;
}
The second option, using a Tuple, is an even more elegant solution than using a userdefined
object. A Tuple can be created to hold any number of values of varying types.
In addition, the data you store in the Tuple is immutable; once you add the data to
the Tuple through the constructor or the static Create method, that data cannot be
changed.
Tuples can accept up to and including eight separate values. If you need to return
more than eight values, you will need to use the special Tuple class:
Tuple Class
When creating a Tuple with more than eight values, you cannot use the static Create
method—you must instead use the constructor of the class. This is how you would
create a Tuple of 10 integer values:
var values = new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>> (
1, 2, 3, 4, 5, 6, 7, new Tuple<int, int, int> (8, 9, 10));
Of course, you can continue to add more Tuples to the end of each embedded Tuple,
creating any size Tuple that you need.
Only for prototyping - Tuples are meaningless. It convenient to use them but it's a shortcut only! For prototypes - fine. Just be sure to delete this code later.
It easy to write, hard to read. It has no visible advantages over classes, inner classes , anonymous classes etc.
Well I tried 3 ways to solve the same problem in C#7 and I have found a use case for Tuples.
Working with dynamic data in web projects can sometimes be a pain when mapping etc.
I like the way the Tuple just auto mapped onto item1, item2, itemN which seems more robust to me than using array indexes where you might get caught on an out of index item or using the anonymous type where you may misspell a property name.
It feels like a DTO has been created for free just by using a Tuple and I can access all the properties using itemN which feels more like static typing without having to create a separate DTO for that purpose.
using System;
namespace Playground
{
class Program
{
static void Main(string[] args)
{
var tuple = GetTuple();
Console.WriteLine(tuple.Item1);
Console.WriteLine(tuple.Item2);
Console.WriteLine(tuple.Item3);
Console.WriteLine(tuple);
Console.WriteLine("---");
var dyn = GetDynamic();
Console.WriteLine(dyn.First);
Console.WriteLine(dyn.Last);
Console.WriteLine(dyn.Age);
Console.WriteLine(dyn);
Console.WriteLine("---");
var arr = GetArray();
Console.WriteLine(arr[0]);
Console.WriteLine(arr[1]);
Console.WriteLine(arr[2]);
Console.WriteLine(arr);
Console.Read();
(string, string, int) GetTuple()
{
return ("John", "Connor", 1);
}
dynamic GetDynamic()
{
return new { First = "John", Last = "Connor", Age = 1 };
}
dynamic[] GetArray()
{
return new dynamic[] { "John", "Connor", 1 };
}
}
}
}

c# modifying structs in a List<T>

Short question: How can I modify individual items in a List? (or more precisely, members of a struct stored in a List?)
Full explanation:
First, the struct definitions used below:
public struct itemInfo
{
...(Strings, Chars, boring)...
public String nameStr;
...(you get the idea, nothing fancy)...
public String subNum; //BTW this is the element I'm trying to sort on
}
public struct slotInfo
{
public Char catID;
public String sortName;
public Bitmap mainIcon;
public IList<itemInfo> subItems;
}
public struct catInfo
{
public Char catID;
public String catDesc;
public IList<slotInfo> items;
public int numItems;
}
catInfo[] gAllCats = new catInfo[31];
gAllCats is populated on load, and so on down the line as the program runs.
The issue arises when I want to sort the itemInfo objects in the subItems array.
I'm using LINQ to do this (because there doesn't seem to be any other reasonable way to sort lists of a non-builtin type).
So here's what I have:
foreach (slotInfo sInf in gAllCats[c].items)
{
var sortedSubItems =
from itemInfo iInf in sInf.subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
sInf.subItems.Clear();
sInf.subItems = sortedSubTemp; // ERROR: see below
}
The error is, "Cannot modify members of 'sInf' because it is a 'foreach iteration variable'".
a, this restriction makes no sense; isn't that a primary use of the foreach construct?
b, (also out of spite) what does Clear() do if not modify the list? (BTW, the List does get cleared, according to the debugger, if I remove the last line and run it.)
So I tried to take a different approach, and see if it worked using a regular for loop. (Apparently, this is only allowable because gAllCats[c].items is actually an IList; I don't think it will allow you to index a regular List this way.)
for (int s = 0; s < gAllCats[c].items.Count; s++)
{
var sortedSubItems =
from itemInfo iInf in gAllCats[c].items[s].subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo>();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
//NOTE: the following two lines were incorrect in the original post
gAllCats[c].items[s].subItems.Clear();
gAllCats[c].items[s].subItems = sortedSubTemp; // ERROR: see below
}
This time, the error is, "Cannot modify the return value of 'System.Collections.Generic.IList.this[int]' because it is not a variable." Ugh! What is it, if not a variable? and when did it become a 'return value'?
I know there has to be a 'correct' way to do this; I'm coming to this from a C background and I know I could do it in C (albeit with a good bit of manual memory management.)
I searched around, and it seems that ArrayList has gone out of fashion in favor of generic types (I'm using 3.0) and I can't use an array since the size needs to be dynamic.
Looking at the for-loop approach, the reason (and solution) for this is given in the documentation for the compilation error:
An attempt was made to modify a value
type that is produced as the result of
an intermediate expression but is not
stored in a variable. This error can
occur when you attempt to directly
modify a struct in a generic
collection.
To modify the struct, first assign it
to a local variable, modify the
variable, then assign the variable
back to the item in the collection.
So, in your for-loop, change the following lines:
catSlots[s].subItems.Clear();
catSlots[s].subItems = sortedSubTemp; // ERROR: see below
...into:
slotInfo tempSlot = gAllCats[0].items[s];
tempSlot.subItems = sortedSubTemp;
gAllCats[0].items[s] = tempSlot;
I removed the call to the Clear method, since I don't think it adds anything.
The problem you are having in your foreach is that structs are value types, and as a result, the loop iteration variable isn't actually a reference to the struct in the list, but rather a copy of the struct.
My guess would be the compiler is forbidding you change it because it most likely would not do what you expect it to anyway.
subItems.Clear() is less of a problem, because altho the field may be a copy of the element in the list, it is also a reference to the list (shallow copy).
The simplest solution would probably be to change from a struct to a class for this. Or use a completely different approach with a for (int ix = 0; ix < ...; ix++), etc.
The foreach loop doesn't work because sInf is a copy of the struct inside items. Changing sInf will not change the "actual" struct in the list.
Clear works because you aren't changing sInf, you are changing the list inside sInf, and Ilist<T> will always be a reference type.
The same thing happens when you use the indexing operator on IList<T> - it returns a copy instead of the actual struct. If the compiler did allow catSlots[s].subItems = sortedSubTemp;, you'll be modifying the subItems of the copy, not the actual struct. Now you see why the compiler says the return value is not a variable - the copy cannot be referenced again.
There is a rather simple fix - operate on the copy, and then overwrite the original struct with your copy.
for (int s = 0; s < gAllCats[c].items.Count; s++)
{
var sortedSubItems =
from itemInfo iInf in gAllCats[c].items[s].subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo>();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
var temp = catSlots[s];
temp.subItems = sortedSubTemp;
catSlots[s] = temp;
}
Yes, this results in two copy operations, but that's the price you pay for value semantics.
The two errors you specified have to do with the fact that you are using structs, which in C# are value types, not reference types.
You absolutely can use reference types in foreach loops. If you change your structs to classes, you can simply do this:
foreach(var item in gAllCats[c].items)
{
item.subItems = item.subItems.OrderBy(x => x.subNum).ToList();
}
With structs this would need to change to:
for(int i=0; i< gAllCats[c].items.Count; i++)
{
var newitem = gAllCats[c].items[i];
newitem.subItems = newitem.subItems.OrderBy(x => x.subNum).ToList();
gAllCats[c].items[i] = newitem;
}
The other answers have better information on why structs work different than classes, but I thought I could help with the sorting part.
If subItems was changed to a concrete List instead of the interface IList, then you'd be able to use the Sort method.
public List<itemInfo> subItems;
So your whole loop becomes:
foreach (slotInfo sInf in gAllCats[c].items)
sInf.subItems.Sort();
This won't require the contents of the struct to be modified at all (generally a good thing). The struct's members will still point to exactly the same objects.
Also, there are very few good reasons to use struct in C#. The GC is very, very good, and you'd be better off with class until you've demonstrated a memory allocation bottleneck in a profiler.
Even more succinctly, if items in gAllCats[c].items is also a List, you can write:
gAllCats[c].items.ForEach(i => i.subItems.Sort());
Edit: you give up too easily! :)
Sort is very easy to customise. For example:
var simpsons = new[]
{
new {Name = "Homer", Age = 37},
new {Name = "Bart", Age = 10},
new {Name = "Marge", Age = 36},
new {Name = "Grandpa", Age = int.MaxValue},
new {Name = "Lisa", Age = 8}
}
.ToList();
simpsons.Sort((a, b) => a.Age - b.Age);
That sorts from youngest to oldest. (Isn't the type inference good in C# 3?)

Categories