I want to convert this statement to build a list of VM images to use in testing into something more elegant using the yield keyword, but the syntax is elluding me.
Desired end goal.
List<VmImages> images;
images[0] - WindowsServer2019
images[1] - WindowsServer2016
images[2] - RhelServer;
images[3] - OpenLogic;
Today the code looks like this:
for (var i = 0; i < LinuxVMs; i++)
{
switch (i)
{
case 0:
linuxDistros.Add(ConfigLoader.redHat);
break;
case 1:
linuxDistros.Add(ConfigLoader.openLogic);
break;
case 2:
linuxDistros.Add(ConfigLoader.suse);
break;
case 3:
linuxDistros.Add(ConfigLoader.ubuntu);
break;
}
}
This feels like a good case to use the yield keyword to simplify the logic into something like this, where I call GetLinuxVMs() for x number of times, where X is the count of LinuxVMs.
private static IEnumerable<VmDistribution> GetLinuxVmDistros()
{
yield return ConfigLoader.redHat;
yield return ConfigLoader.openLogic;
yield return ConfigLoader.suse;
yield return ConfigLoader.canonical;
}
I'm not sure how to integrate this into my code, this is what I've tried:
for (var i = 0; i < LinuxVMs; i++)
{
linuxDistros.Add(GetLinuxVmDistros());
}
Since I get an IEnum back from the GetLinuxVmDistros method every time, I am puzzled as to how this is supposed to work at all.
From your stated "desired end goal"
List<VmImages> images = new() {
WindowsServer2019,
WindowsServer2016,
RhelServer,
OpenLogic
}
All the rest of the looping/finagling is just confusing the issue, IMHO
GetLinuxVmDistros() will return an IEnumerable<VmDistribution> as per your definition.
It seems you want to add to a collection called linuxDistros another collection.
If linuxDistros is a List<VmDistribution>, simply use the AddRange method:
linuxDistros.AddRange(GetLinuxVmDistros());
See: https://learn.microsoft.com/fr-fr/dotnet/api/system.collections.generic.list-1.addrange?view=net-6.0
But I fail to see the point of creating an Enumerable just for this.
I'm not certain what you try to achieve since the naming is .. not clear. But with this code below, it works:
class ConfigLoader
{
static public VmDistribution redHat { get; set; } = VmDistribution.a;
static public VmDistribution openLogic { get; set; } = VmDistribution.b;
static public VmDistribution suse { get; set; } = VmDistribution.c;
static public VmDistribution canonical { get; set; } = VmDistribution.d;
}
enum VmDistribution { a, b, c, d }
IEnumerable<VmDistribution> GetLinuxVmDistros()
{
yield return ConfigLoader.redHat;
yield return ConfigLoader.openLogic;
yield return ConfigLoader.suse;
yield return ConfigLoader.canonical;
}
var list = new List<VmDistribution>();
list.AddRange(GetLinuxVmDistros());
Notice that in the end I'm invoking GetLinuxVmDistros() only once, and the list is filled with method list.AddRange().
In your example you should do whatever you need to do with the items with the loop.
for (var i = 0; i < LinuxVMs; i++)
{
ProcessItem(LinuxVMs[i]);
}
Having said that, there's isn't much point in yielding on such a small set of results. The main purpose of yield return is to allow a large collection to be returned 1 item at a time, where the calling code may want to stop iterating over the items at any point. Another usage is where fetching all items at once may be very expensive resource-wise.
For example, you may not want to continue if 1 item couldn't be processed for whatever reason.
for (var i = 0; i < LinuxVMs; i++)
{
var result = ProcessItem(LinuxVMs[i]);
if (!result.Success)
{
break;
}
}
However, if you just need the items in a list then forget all of that and just create the list with the items in it straight away:
var images =
new List<VmImages>
{
WindowsServer2019,
WindowsServer2016,
RhelServer,
OpenLogic
};
Related
Hopefully this post gives more clarity as to what I am trying to achieve.
Objective: I want to spawn 20 apples(that have an attached button) from a list at runtime. When the apples are clicked they will spawn a popup with information pertaining to the apple that was clicked.
What I'm doing currently: I am using a for loop to run through the list to spawn the apples. I currently have the following code:
public class AppleInventory : MonoBehaviour
{
[SerializeField] private ApplesScript applPrefab;
[SerializeField] private Transform applParent;
public ApplesScript CreateApples()
{
var appl = Instantiate(applPrefab, applParent);
for (int i = 0; i < apples.Count; i++)
{
appl = Instantiate(applPrefab, applParent);
appl.InitAppleVisualization(apples[i].GetAppleSprite());
appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(i));
appl.transform.position = new Vector2(apples[i].x, apples[i].y);
}
return appl;
}
}
The Problem: The problem is that when I use the for loop and click on the button,it returns the following error: ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. The popup information also does not update.
Code without for loop: The code works to spawn one apple when I remove the for loop and set the int i = to a specific number, like below. It will give the correct popup info for any number that "i" is set to. This lets me know that it is not the rest of the code that is the issue. This leads me to believe it is the "return" line along with the for loop that is the issue. It seems I may need to "return" for each iteration but I am unsure of how to go about doing this.
public ApplesScript CreateApples()
{
int i = 7;
var appl = Instantiate(applPrefab, applParent);
appl.InitAppleVisualization(apples[i].GetAppleSprite());
appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(i));
appl.transform.position = new Vector2(apples[i].x, apples[i].y);
return appl;
}
Thank you,
-
UPDATE
The fix was so simple. I just ended up creating a new method specifically for the for loop and it worked the way I wanted. My code now looks like this:
public void StarterOfApplesCreation()
{
for (int i = 0; i < apples.Count; i++)
{
CreateApples(i);
}
}
public void CreateApples(int i)
{
var appl = Instantiate(applPrefab, applParent);
appl.InitAppleVisualization(apples[i].GetAppleSprite());
appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(i));
appl.transform.position = new Vector2(apples[i].x, apples[i].y);
}
You have two options. The conventional option is to create all the items first and then return them all in some sort of list, e.g.
public static void Main()
{
foreach (var thing in GetThings(5))
{
Console.WriteLine(thing.Number);
}
Console.ReadLine();
}
public static Thing[] GetThings(int count)
{
var things = new Thing[count];
for (var i = 0; i < count; i++)
{
things[i] = new Thing { Number = i };
}
return things;
}
The more modern option is to use an iterator. It actually will return one item at a time. It has the limitation that you have to use the items there and then - you won't have random access like you would an array or the like - but it also has advantages, e.g.
public static void Main()
{
foreach (var thing in GetThings(5))
{
Console.WriteLine(thing.Number);
}
Console.ReadLine();
}
public static IEnumerable<Thing> GetThings(int count)
{
for (var i = 0; i < count; i++)
{
var thing = new Thing { Number = i };
yield return thing;
}
}
The result of an iterator will usually be used as the source for a foreach loop or a LINQ query. Note that you can always call ToArray or ToList on the result of an iterator if you do want random access in specific situations, but you still have the advantages of an iterator elsewhere. For instance, let's say that your method produces 1000 items and you want to find the first one that matches some condition. Using my first example, you would have to create all 1000 items every time, even if the first one was a match. Using an iterator, because the items are processed as they are created, you can abort the process as soon as you find a match, meaning that you won't unnecessarily create the remaining items.
Note that my examples use the following class:
public class Thing
{
public int Number { get; set; }
}
You can copy and paste the code into a Console app that doesn't use top-level statements. The bones of the code will still work with top-level statements, but you'll need to make a few other modifications.
Store each separate "appl" that gets instantiated in an Array, ie appls[i]=appl
Do this within the for loop.
If you think about it, by putting the line "return appl;" outside the for loop, you are only storing that last game object, not all of them. Thats why creating an array of gameobjects and assigning them within the loop may work for you.
For a project we've been working on we used a concurrent dictionary, which was fine until a new specification came up which required the dictionary to be sorted (it should remain in the order it was added, kind of like a FIFO).
This is currently what we do, we take an x amount (5 in this case) of items out of the dictionary:
private Dictionary<PriorityOfMessage, ConcurrentDictionary<Guid, PriorityMessage>> mQueuedMessages = new Dictionary<PriorityOfMessage, ConcurrentDictionary<Guid, PriorityMessage>>();
var messages = new List<KeyValuePair<Guid, PriorityMessage>>();
messages.AddRange(mQueuedMessages[priority].Take(5));
then we do some things with it and eventually if everything succeeds we removed them.
mQueuedMessages[priority].TryRemove(messageOfPriority.Key);
However if things fail we won't remove them and try later. So unfortunatly there is no concurrent sorted dictionary, but are there ways to ensure the messages stay in the order they are added?
It is very important we can take multiple objects from the list/dictionary without removing them (or we need to be able to add them to the front later).
How often will you take per second?
.
it could be a thousand times a second
1000 lock operations per second are absolutely nothing. This will consume almost no time at all.
my colleague has already tried using locks and lists and he deemed it too slow
In all likelihood this means that the locked region was too big. My guess is it went something like that:
lock (...) {
var item = TakeFromQueue();
Process(item);
DeleteFromQueue(item);
}
This does not work because Process is too slow. It must be:
lock (...)
var item = TakeFromQueue();
Process(item);
lock (...)
DeleteFromQueue(item);
You will not have any perf problems with that at all.
You can now pick any data structure that you like. You are no longer bound by the capabilities of the built-in concurrent data structures. Besides picking a data structure that you like you also can perform any operation on it that you like such as taking multiple items atomically.
I have not fully understood your needs but it sounds like SortedList might go in the right direction.
You could also go for another solution (haven't tested it performance-wise):
public class ConcurrentIndexableQueue<T> {
private long tailIndex;
private long headIndex;
private readonly ConcurrentDictionary<long, T> dictionary;
public ConcurrentIndexableQueue() {
tailIndex = -1;
headIndex = 0;
dictionary = new ConcurrentDictionary<long, T>();
}
public long Count { get { return tailIndex - headIndex + 1; } }
public bool IsEmpty { get { return Count == 0; } }
public void Enqueue(T item) {
var enqueuePosition = Interlocked.Increment(ref tailIndex);
dictionary.AddOrUpdate(enqueuePosition, k => item, (k, v) => item);
}
public T Peek(long index) {
T item;
return dictionary.TryGetValue(index, out item) ?
item :
default(T);
}
public long TryDequeue(out T item) {
if (headIndex > tailIndex) {
item = default(T);
return -1;
}
var dequeuePosition = Interlocked.Increment(ref headIndex) - 1;
dictionary.TryRemove(dequeuePosition, out item);
return dequeuePosition;
}
public List<T> GetSnapshot() {
List<T> snapshot = new List<T>();
long snapshotTail = tailIndex;
long snapshotHead = headIndex;
for (long i = snapshotHead; i < snapshotTail; i++) {
T item;
if (TryDequeue(out item) >= 0) {
snapshot.Add(item);
}
}
return snapshot;
}
}
I have a list of windows but it is not in the order I want them. I'm able to get the windows into string from the title - they are being put into a list of windows. I want to sort this list in a specific order with Estimate 1st, Control Center 2nd, and Login 3rd. This is the order I desire. I have know idea on how to go about it but I want to sort it before it goes into the foreach loop.
private void CloseMainWindows(IEnumerable<Window> Windows)
{
var winList = Windows.ToList();
winList.Sort()//This is where I want to sort the list.
foreach (Window window in winList)
{
if (window.Title.Contains("Estimate"))
{
Estimate.closeEstimateWindow();
}
if (window.Title.Contains("Control Center"))
{
ContorlCenter.CloseContorlCenter();
}
if (window.Title.Contains("Login"))
{
login.ClickCanel();
}
}
}
One way would be to have a lookup function:
int GetTitleIndex(string s)
{
if (s.Contains("Estimate")) return 0;
if (s.Contains("Control Center")) return 1;
if (s.Contains("Login")) return 2;
}
Then, to sort, you lookup the indexes:
winList.Sort((x, y) => GetTitleIndex(x).CompareTo(GetTitleIndex(y)));
Alternatively, you could create the list directly using LINQ's OrderBy:
var winList = Windows.OrderBy(GetTitleIndex).ToList();
And in fact in your case you don't even need the intermediate list:
foreach (var window in Windows.OrderBy(GetTitleIndex))
{
...
}
You can do something like this :
List<Type> data = new List<Type>();
data.Sort(new Comparison<Type>(Compare));
private static int Compare(Type x, Type y)
{
//you can compare them like so :
//I'll show you an example just for the sake of illustrating how :
if(x.Name.ToString().Length > y.Name.ToString().Length) return 1;
else return -1;
//the logic for the comparison is up to you.
//compare the 2 elements.
}
I have a problem with slow "building" a list and I don't have idea how to speed it up.
Here is my code:
private static ConcurrentBag<Classe<PojedynczeSlowa>> categoryClasses = new ConcurrentBag<Classe<PojedynczeSlowa>>();
private const int howManyStudents = 20;
private static int howManyClasses;
private static EventWaitHandle[] ewhClass;
private static List<Classe<Words>> deserializeClasses;
//...
public static void CreateCategoryClasses()
{
deserializeClasses = Deserialize();
howManyClasses = deserializeClasses.Count;
ewhClass = new EventWaitHandle[howManyClasses];
for (var i = 4; i >= 0; --i)
{
categoryClasses.Add(new Classe<PojedynczeSlowa>(((Categories) i).ToString()));
}
WaitCallback threadMethod = ParseCategories;
ThreadPool.SetMaxThreads(howManyStudents, howManyClasses);
for (var i = 0; i < howManyClasses; ++i)
{
ewhClass[i] = new EventWaitHandle(false, EventResetMode.AutoReset);
ThreadPool.QueueUserWorkItem(threadMethod, i);
}
for (var i = 0; i < howManyClasses; ++i)
{
ewhClass[i].WaitOne();
}
var xmls = new XmlSerializer(typeof(List<Classe<PojedynczeSlowa>>)); //poprawić!!
using (var sw = new StreamWriter(#"categoryClasses.xml"))
{
xmls.Serialize(sw, categoryClasses.ToList());
}
}
private static void ParseCategories(object index)
{
int sum;
var i = index as int?;
if (deserializeClasses[i.Value].Category == Categories.PEOPLE.ToString())
{
foreach (var word in deserializeClasses[i.Value].Bag)
{
sum =
deserializeClasses.Count(
clas =>
clas.Bag.Where(x => clas.Category == deserializeClasses[i.Value].Category)
.Contains(word));
if (!categoryClasses.ElementAt(0).Bag.Contains(new PojedynczeSlowa(word.Word, sum)))
{
categoryClasses.ElementAt(0)
.Bag.Add(new PojedynczeSlowa(word.Word,
Convert.ToDouble(sum)/
Convert.ToDouble(deserializeClasses.Count(x => x.Category == deserializeClasses[i.Value].Category))));
}
}
}
//rest of the code which adds elements to the list on other indexes.
ewhClass[(i).Value].Set();
}
I might add that:
deserializeClasses contains about 18550 elements of class "Word", and any of this elements ("Word") contains a list of string and int, average size of this list is about 200-250 elements. I use .net 4.5.1
Thanks for help!
A couple things (I don't have enough rep to comment so my comments are coming in here too)...
1) Class definitions would be very helpful. For example, you have
if (!categoryClasses.ElementAt(0).Bag.Contains(new PojedynczeSlowa(word.Word, sum)))
which will never be true if you haven't overridden object.Equals (did you?). Also, it's much harder to know what's going on with an incomplete sample.
2) Your code
sum = deserializeClasses.Count(clas => clas.Bag.Where(x => clas.Category == deserializeClasses[i.Value].Category).Contains(word));
doesn't make use of x at all. Consider
sum = deserializeClasses.Count(clas => clas.Category == deserializeClasses[i.Value].Category && clas.Bag.Contains(word));
This avoids much potential enumeration and could speed up the average cost even though the worst case cost remains the same.
3) Dictionaries are your friend. Consider making some temp dictionaries that are indexed by whatever you're checking against. I'm having a hard time figuring out exactly what you're trying to do (see comment 1) but I'm guessing you could save quite a bit of performance cost, particularly that Contains() call, with using a Dictionary.
4) I'm not sure that multithreading is going to save you anything here. I'm guessing it will make things slower since this looks to be CPU bound and you are adding CPU overhead with thread switching.
I would help out with some code but I'm in a bit of a hurry and don't have time to guess at the rest of the missing code to get everything to compile.
I need to iterate over a list (or whatever enumeration), but I'd like to add values into the list in the course of the iteration.
This is an example.
public static void RunSnippet()
{
List<int> hello = new List<int>();
hello.Add(1); hello.Add(2); hello.Add(3);
foreach (var x in hello)
{
Console.WriteLine(x);
if (x == 1) {
hello.Add(100);
}
}
}
I expect to get "1,2,3,100", but instead I got this error.
How can I iterate over a list that is changing in the process?
ADDED
What I want to accomplish is that I iterate over elements to process something. The thing is that some of the elements needs to be decomposed into sub elements on and on.
public static void RunSnippet()
{
List<Element> hello = new List<Element>();
hello.Add(Element); hello.Add(Element); hello.Add(Element);
foreach (var x in hello)
{
List<Element> decomposed;
decomposed = Decompose(x);
if (decomposed != null) {
foreach (var y in decomposed)
{
hello.Add(y);
}
}
}
}
You can't, basically. Not with a foreach loop, anyway. You can use a straight for loop:
for (int i = 0; i < hello.Count; i++)
{
int x = hello[i];
Console.WriteLine(x);
if (x == 1) {
hello.Add(100);
}
}
I would personally try to avoid doing it in the first place though - it can get very hard to reason about whether you'll ever complete, or if you'll skip items (if you're removing instead of adding, or adding before your current position).
You can't. You should create a new list and store the values in there.
public static void RunSnippet()
{
List<int> hello = new List<int>();
List<int> additions = new List<int>();
hello.Add(1); hello.Add(2); hello.Add(3);
foreach (var x in hello)
{
Console.WriteLine(x);
if (x == 1) {
additions.Add(100);
}
}
hello.AddRange(additions);
}
Use a snapshot of it instead:
foreach (var x in hello.ToArray())
{
// whatever here
}
Problem solved! Well, in a way. Items added during iteration would not be included.
No, you can't iterate over a list and modify them in the same iteration. Use a new list instead.
I found that there is stack in C#. I guess I could use stack.
public static void RunSnippet()
{
Stack<int> hello = new Stack<int>();
hello.Push(1); hello.Push(2); hello.Push(3);
while (hello.Count > 0)
{
int x = hello.Pop();
Console.WriteLine(x);
if (x == 1) {
hello.Push(100);
}
}
}
Using foreach you can't! You could use a for-loop, but it's very very bad style to do things like this. Things like this make your code very error prone, unpredictable and hard to debug.
There are answers that claims what you want cannot be achieved with foreach. That claim is wrong, all you need to do is to write a custom class with a custom enumerator.
public class CustomList : IEnumerable<int>
{
readonly List<int> list = new List<int>{1,2,3,4};
private int now = 0;
public void Add(int n)
{
list.Add(n);
}
public IEnumerator<int> GetEnumerator()
{
while (now<list.Count)
{
yield return list[now];
now++;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Now the following piece of code will print 1,2,3,4 and 100 to the screen:
var list = new CustomList();
foreach (int n in list)
{
if(n==1)
list.Add(100);
Console.WriteLine(n);
}
But I write this only as a proof of concept. You don't want to do this. If you will only add new items on the back, use Queue as others has said. If you will always add new items on the front, use Stack. If you will need both, write a custom LinkedList class with Dequeue (=Pop), Enqueue and Push operations, use something like :
while(list.notEmpty())
var item = list.Dequeue();
//bla bla
and you are all set. (You could even write a custom Enumerator again, to use with foreach, but we are destructing the list as we go, so it is against the spirit of Enumerations, and why bother in any case)