I'm going through another developer's code, which is shown below:
[XmlElement("AdminRecipient")] public AdminRecipient[] AdminRecipientCollection = new AdminRecipient[0];
public AdminRecipient this[ string type ]
{
get
{
AdminRecipient result = null;
foreach( AdminRecipient emailRecipient in AdminRecipientCollection )
{
if( emailRecipient.Type == type )
{
result = emailRecipient;
break;
}
}
return( result );
}
Can someone explain what's going to happen in this line?
public AdminRecipient[] AdminRecipientCollection = new AdminRecipient[0];
The XML file that contains all of the email recipients has about 5 email addresses. But by using [0], will the foreach loop return each of those email addresses?
I have a basic understanding of indexers, but I don't this. What does it do?:
public AdminRecipient this[ string type ]
At the end of the day, the problem here is that the application doesn't send out an email when all 5 recipients are in the xml file. If I replace the 5 addresses with just 1 email addresses, then I'm able to get the email (which leads me to believe that there's a logic issue somewhere here).
An indexer allows you to use a type with the same syntax as array access. One of the simplest examples would be List<T>:
List<string> x = new List<string>();
x.Add("Item 0");
x.Add("Item 1");
string y = x[0]; // "Item 0"
That use of x[0] is calling the indexer for List<T>. Now for List<T> the index is an integer, as it is for an array, but it doesn't have to be. For example, with Dictionary<TKey, TValue> it's the key type:
Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("foo", "bar");
string fetched = dictionary["foo"]; // fetched = "bar"
You can have a "setter" on an indexer too:
dictionary["a"] = "b";
Your indexer is just returning the first AdminRecipient in the array with a matching type - or null if no match can be found.
(It's unfortunate that the code you've shown is also using a public field, by the way. It would be better as a property - and probably not an array, either. But that's a separate discussion.)
EDIT: Regarding the first line you highlighted:
public AdminRecipient[] AdminRecipientCollection = new AdminRecipient[0];
That will create an array with no elements, and assign a reference to the AdminRecipientCollection field. With no further changes, the foreach loop would not have anything to iterate over, and the indexer will always return null.
However, presumably something else - such as XML serialization - is assigning a different value to that field - populating it with more useful data.
The line you're asking about is an assignment of AdminRecipientCollection to an empty array. This assignment will occur at instantiation time. The deserialization of the XML will happen after the class is instantiated, if ever. The author's purpose here is presumably to ensure that AdminRecipientCollection will never be null, even in case the class is used before XML is deserialized into it.
By ensuring that AdminRecipientCollection is never null, the author can ignore the possibility that the foreach loop will throw a NullReferenceException. At least, that's probably the hope.
Unfortunately, because AdminRecipientCollection is public, the AdminRecipient property could still throw, as in the following case:
var demoClass = new DemoClass();
demoClass.AdminRecipientCollection = null;
var firstRecipient = demoClass[0];
To prevent this possibility, remove the initial assignment and check for null in the AdminRecipient property before beginning the enumeration.
Related
I have a value I'm getting dynamically. This is because it exists in an abstraction method I'm using to build a query from a dot-notation representation of deeply related property.
I need to know if the value contains a particular value. This is easy enough for a string, but when the object in question is a collection I can't figure how to check. I can assume it's a List in my situation, but I don't know what type of list, so I can't achieve a property casting with something like List<int>.
So where x is an object and spec is dynamic this is how I test for string:
return x.ToString()?.Contains(spec, StringComparison.InvariantCultureIgnoreCase) ?? false;
This successfully determines if it's a collection type, but I still can't call Contains on it:
if (x.GetType().Namespace == "System.Collections.Generic")
{
return x.Contains(spec); //does not work
}
Also tried:
var list = (List<dynamic>)x;
var list = (List<object>)x;
But this fails in my test scenario with List<int>
I have a list of integers (Levels). I want to initialize a nested Object of Filter called myFilter as below(Filter is a class with two properties: Value and NextFilter):
var myFilter = new Fliter{
Value = Levels[0],
NextFilter = new Filter{
Value = Levels[1],
NextFilter = new Filter{
Value = Levels[2],
NextFilter = new Filter{
Value = Levels[n],
NextFilter = null
}
}
}
}
Level's count is not static and depends on the input list (I have a multi select list that generates Level)
How can I do that?
This is a classic event for using - the technique of a method that calls itself:
public static Filter CreateFilter(List<int> values) => values.Any() ? new Filter //If the list contains elements, create the filter
{
Value = values.First(), //assign the first item of the values to the value property
NextFilter = CreateFilter(values.Skip(1).ToList()) //Create the rest of the nested object with the rest of the values
} : null; //If there aren't any items left in the list, return null and stop the recursion
You could of course do it in the constructor as well:
public Filter(List<int> values)
{
if (!values.Any()) return;
Value = values.First();
NextFilter = values.Count > 1 ? new Filter(values.Skip(1).ToList()) : null;
}
For more information about recursion, take a look at this: https://www.dotnetperls.com/recursion, for more information on nested classes read through this: https://www.dotnetperls.com/nested-class.
A few more information on recursion:
You can actually achieve everything through recursion - you don't even need loops. That's the reason why in languages like Haskell loops don't exist.
The simplest recursive function is:
public static void EndlessLoop()
{
//Loop body
EndlessLoop();
}
However, even Resharper suggests to convert it to a loop:
Another example, if you want to get the sum of a list you could do:
public static int Sum(List<int> summands) => summands.Count > 0
? summands.First() + Sum(summands.Skip(1).ToList())
: 0;
But those examples aren't useful in C#, as C# isn't a functional programming language, which causes recursion to be slower than loops. Furthermore recursion often causes a StackOverflowException (fitting to this site). If you run the endless loop recursion, it doesn't even take a second till your stack is full.
The reason for this is, that C# adds the address, from which a method got called, to the stack. If a method is called very often (and in 1 second a lot of recursive calls are made) a lot of addresses are added to the stack, so that it overflows.
However I still think, even though those examples aren't useful in c#, that it's quite useful to be able to handle recursion. Recursion is for example the only way to explore a directory structure, for getting for example all files:
public static List<FileInfo> GetAllFiles(DirectoryInfo directory) => directory.GetFiles()
.Concat(directory.GetDirectories().SelectMany(GetAllFiles))
.ToList();
And, as you experienced, it's the only way to fill a nested class from a list properly.
Just make a constructor of Filter, that will get Levels array as a parameter, that will set it's Value as level[0], and init NextFilter = new Filter(level.Skip(1)). Something like that. And it will recursively initialize your object.
First of all I will say that I've changed my design and no longer need that but getting a good answer for that will still be nice
I have the following class, ListContainer, in my code (The attached codes are all mcve):
class ListContainer
{
public object ContainedList
{
get; private set;
}
public int Value
{
get; private set;
}
public ListContainer(object list, int value)
{
ContainedList = list;
Value = value;
}
}
And in some other class in my code I have a List<ListContainer> and I need each ListContainer to contain this List<ListContainer>, so I can implement it like that:
//Field in the class
List<ListContainer> mContainers = null;
//In the constructor:
mContainers = new List<ListContainer>();
mContainers.Add(new ListContainer(mContainers, SOME_CONST));
mContainers.Add(new ListContainer(mContainers, SOME_OTHER_CONST));
Than it works fine, but when I've tried to use list initializer:
//Field in the class
List<ListContainer> mContainers = null;
//In the constructor:
mContainers = new List<ListContainer>
{
new ListContainer(mContainers, SOME_CONST),
new ListContainer(mContainers, SOME_OTHER_CONST)
}
You would expect the results to be equivalent but in reality the result looks like that:
mContainers
[0] - ListContainer
ContainedList = null
Value = SOME_CONST
[1] - ListContainer
ContainedList = null
Value = SOME_OTHER_CONST
Seeing this results I've inspected the output MSIL of this C# compilation and seen the following code:
Now, this explains why the problem occurs, and I've even checked out in the CSharp Language Specification document and this is the defined behavior:
A List can be created and initialized as follows:
var contacts = new List<Contact> {
new Contact {
Name = "Chris Smith",
PhoneNumbers = { "206-555-0101", "425-882-8080" }
},
new Contact {
Name = "Bob Harris",
PhoneNumbers = { "650-555-0199" }
}
};
which has the same effect as
var __clist = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
__clist.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
__clist.Add(__c2);
var contacts = __clist;
where __clist, __c1 and __c2 are temporary variables that are otherwise invisible and inaccessible.
So obviously this behaviour is intended. Is there a good reason everything is done on the temporary variable and not on the original one? since it seems like a wrong behaviour to me.
The reason is to avoid race conditions with concurrent threads accessing the original variable where you would add your elements. An inconsistency would appear if a thread accesses the variable while not all elements are added to it yet.
Two threads accessing the same variable would therefore get an inconsistent list, with different elements in it.
This would not come as a shock if the elements are added on different lines, but since you use an object initializer, it is normal to perceive the object as directly initialized with all its elements in it, hence the need of a temporary, invisible, variable.
Is there a good reason everything is done on the temporary List and not on the original one?
There is no original list:
var __clist = new List<Contact>();
// …
__clist.Add(__c1);
// …
__clist.Add(__c2);
var contacts = __clist;
Only one list is ever created. What you probably mean is that it’s done on a temporary variable instead of the original variable, but that has no practical effect—other than probably being easier to implement. This is especially true if you think about that collection initialization is not limited to the context of variable assignments:
SomeMethodCall(new List<int>() { 1, 2, 3 });
Since there is no reference to that list, the simplest solution to implement this is just to use a temporary variable that holds the list, and put that variable at the place of the initializer then.
What’s also important about this is that the old value is completely overwritten. So in your mContainers = new List<ListContainer>, the old value of mContainers is never being looked at for the purpose of the initializer syntax.
It’s probably a good idea to think about the collection initialization as an “atomic” operation. The list only exists (at least to you) once the whole initializer completes. So you cannot reference itself from within the initializer.
Assignments are first evaluated on the right side of the = and then the assignment takes place. So mContainers is still null.
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 ;)
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?)