How can I utilize Polymorphism with List objects? - c#

Here's a brief overview of my following problem, in case I am approaching this incorrectly:
I have a list of customers, purchase orders, and invoices all displayed in datagrids. I would like to verify that what is displayed in these grids matches the lists I actually have (this is for an automated testing application). The way I am trying to handle it, is by going through each table, and within that going through each row of objects, and comparing it to the objects in my lists (hence the actual/expected variables).
I have an application that has multiple List objects, for example:
List<PurchaseOrder>
List<Customer>
List<Invoice>
And I want to be able to write a loop that can manipulate each of these lists, rather than having one foreach statement for each list. I have multiple tables that show each object (a PO table, a customer table, etc) and I want to iterate all of them as such:
foreach(Table t in Tables)
{
List<??> tableItems = new List<??>; // Perhaps this should be `object tableItems;`?
switch(t.Name)
{
case "PurchaseOrder":
tableItems = purchaseOrders; // purchaseOrders is a List<PurchaseOrder> object
break;
case "Customer":
tableItems = customers; // List<Customer>
break;
case "Invoice":
tableItems = invoices; // List<Invoice>
break;
default:
break;
}
// Now I want to get the count of items, to loop through
for(int i = 0; i < tableItems.Count; i++)
{
// Do work
}
}
However, I can't figure out the right way to use the polymorphism here. If I make tableItems a List of Objects (List<Object>), I will get the error Cannot convert type of List<PurchaseOrder> to List<Object>.
If I cast tableItems to the Object class, I can't call the .Count field.
EDIT: I understand I could solve this easily by putting my for loop inside each case statement -
case "PurchaseOrder":
tableItems = purchaseOrders;
for(int i = 0; i < tableItems.Count; i++)
{
// Do Work
}
break;
But I would like a way to extract that for loop.
EDIT 2
Here's another example of the code I'm using now and what I would like to do with it but can't quite figure out:
foreach(Table t in Tables)
{
List<??> tableItems = new List<??>; // Perhaps this should be `object tableItems;`?
string expectedItem;
string actualItem = t.rows[currentRow].Cells[column1].Value.ToString();
switch(t.Name)
{
case "PurchaseOrder":
for(int i = 0; i < purchaseOrders.Count; i++)
{
PurchaseOrder p = purchaseOrders[i];
expectedItem = p.POValue1;
}
break;
case "Customer":
for(int i = 0; i < customers.Count; i++)
{
Customer c = customers[i];
expectedItem = c.CustValue1;
}
break;
case "Invoice":
for(int i = 0; i < invoices.Count; i++)
{
Invoice inv = invoices[i];
expectedItem = inv.InvoiceValue1;
}
break;
default:
break;
}
// I would prefer to do something like this:
switch(t.Name)
{
case "PurchaseOrder":
actualItem = purchaseOrders; // List<PurchaseOrder> object
break;
case "Customer":
actualItem = customers; // List<Customer>
break;
case "Invoice":
actualItem = invoices; // List<Invoice>
break;
default:
break;
}
for(int i = 0; i < tableItems.Count; i++)
{
(object) o = tableItems[i];
switch(object)
{
case "PurchaseOrder":
expectedItem = o.POValue1;
break;
case "Customer":
expectedItem = o.CustValue1;
break;
case "Invoice":
expectedItem = inv.InvoiceValue1;
break;
default:
break;
}
}
}

Instead of intermingling code that handles all tables simultaneously, I would solve the problem one table at a time.
Each object type dictates what is displayed in the table. This will always be a tuple of any kind of objects. So it is sufficient if the validation method gets these display objects instead of the source:
//More or less pseudo code
bool Validate(Table table, object[][] displayObjects)
{
for each iItem
for each iColumn
if(table.Rows[iItem].Columns[iColumn].Equals(displayObjects[iItem][iColumn])
//everything is fine
else
//there is a validation error
}
This leaves us with the task to transform any list of objects to its respective list of display objects. This can be performed with LINQ:
Validate(POTable, POList.Select(po =>
new object[] { po.Property1, po.Property2, po.Property3 }).ToArray());
// ...

OP. This seems like an XY question here. Your domain problem is that you would like to automate comparing a DataSet with a collection of collections of objects. You have noted that there are commonalities in the code for comparing PurchaseOrders with the PurchaseOrder table and Invoices with the Invoice table. I suggest you read up on design patterns and see if there is a pattern that would work with your problem.
The Strategy pattern looks to be pretty good for it...
public bool TableCollectionCompare<T>(
Compare<DataRow, T> comparer,
DataTable table,
ICollection<T> objects);
Another pattern would be an abstract class to do the loading of the Table and the collection...
public abstract class TableCollectionComparer<T>
{
protected bool Compare(DataRow row, T item);
public bool Compare(DataTable table, ICollection<T> item)
{
foreach(DataRow row in table.Rows)
{
...
bool result = Compare(row, item);
}
}
}

Related

C#, converting a case statement to a yield statement

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
};

Does expanding arraylist of objects make a new object?

Assume we have an array list of type Employe , does expanding it's length by 1 make a new object in the list ?
is the code in else statement correct? and is it recommended?
public void ModifierEmp(int c)
{
for(int i = 0; i < Ann.Count; i++)
{
if(Ann[i].Code == c)
{
Ann[i].saisie();
} else
{
i = Ann.Count + 1; //expanding arraylist ann
Ann[i].saisie(); //saisie a method for the user to input Employe infos
}
}
}
https://imgur.com/VfFHDKu "code snippet"
i = Ann.Count + 1;
The code above is not expanding the list: it is only setting your index variable (i) to have a new value.
If you wanted to make the list bigger, you would have to tell it which object to put into that new space you create. For example:
Ann.Add(anotherItem);
Of course, this gives you the ability to decide whether to add an existing item, create a new item (e.g. Ann.Add(new Something() { Code = c })), or even add a null value to the list (which is not usually a good idea).

Switch statement on elements in list

Can I make a dynamics switch statement, i.e if I have a list containing 1,2,3,4,5 rather than manually doing case 1:, case 2: etc can I do it using a for loop as shown below?
The code doesn't work. Visual Studio gives an error saying case has to be a referenced label, I am a beginner.
switch (selectedShow)
{
//Show list is a list of type Shows
for (int i = 0; i < showList.Count; i+=1)
{
case i:
{
waitingList[waitingList.Count].Show = showList[selectedShow];
break;
}
}
}
It kinda seems you just want:
waitingList[waitingList.Count].Show = showList[selectedShow];
Switch statement is used for making a different operations for different values. thats why the "case" select the value to proceed. In your example only one operation is implemented for any value in your list. So, you don't need to apply "if" statement to check the condition where selectedShow is equal to some item in your list. Preferred way to iterate over list in C# is foreach operation. For example:
foreach (var i in showList)
{
if(i == selectedShow)
{
waitingList.Last().Show = i;
break;
}
}
I also replaced unsafe waitingList.[waitingList.Count] for more clear waitingList.Last() method (you may need to add using System.Linq; at the top of your file)
As already specified, you do not need a case statement. You can write a code similar to the following:
//Show list is a list of type Shows
for (int i = 0; i < showList.Count; i+=1)
{
waitingList[waitingList.Count].Show = showList[i];
if (someBreakConditionFunction())
break;
}
This code segment here:
//Show list is a list of type Shows
for (int i = 0; i < showList.Count; i+=1)
{
case i:
{
waitingList[waitingList.Count].Show = showList[i];
break;
}
}
makes no sense, for every value of i you will execute the case condition:
instead do:
for (int i = 0; i < showList.Count; i+=1)
{
waitingList[waitingList.Count].Show = showList[i];
}
switch (selectedShow)
{
//Show list is a list of type Shows
for (int i = 0; i < showList.Count; i+=1)
{
waitingList[waitingList.Count].Show = showList[i];
//Add some condition if you want to break the loop.
if(breakCondition)
break;
}
}
Using a forloop and if statement should work. I can iterate through the list using the for each loop and if the selected show (user selects in terminal) is the current iterated show. Then I can reference the show in the waitingList.
selectedShow = int.Parse(Console.ReadLine());
//Show list is a list of type Shows
for (int i = 0; i <=showList.Count;)
{
if (selectedShow == i)
{
//Count starts from 1 not 0
waitingList[waitingList.Count-1].Show = showList[selectedShow];
break;
}
}

How to iterate over list while removing items at the same time?

I'm trying to find an elegant way to iterate over a list while items are removed at the same time.I know this solution. But my conditions are something harder:
all single-threaded here
Iteration must be forward.
Every item must be processed exactly once.
Multiple and random items can be removed while 1 item is being processed.
Items are complex and smart objects. They execute a custom method and it can decide that some items (0 to all) shall be removed.
(add and insert can happen too, but just now this is not important, in case there is a way to handle this at the same time, that would be great)
Question: Is this possible ? If yes, how ?
I have the idea of marking the objects as removed / inactive. When I iterate again later, I will remove them without calling them to do things. The iteration will be repeated quite often, that's why every object must have exactly 1 turn at each iteration. Would that work ?
This is how I handle things now. It's not perfect but gives you the hint what is asked I hope.
Pseudo-code:
class Foo
{
public void DoStuff()
{
// do other stuff
if (condition)
Kill(x); // should result in list.RemoveAt(x) somehow
}
}
class Program
{
[STAThread]
static void Main(string[] args)
{
List<Foo> list = new List<Foo>();
for (int i = 0; i < 15; i++)
list.Add(new Foo());
for (int i = 0; i < list.Count; i++)
list[i].DoStuff();
Console.ReadKey();
}
}
(This is not an XY Problem. I'm sure. I have this sitting on my mind for years now and I decided to finally find a solid solution. I'm working in C# for this. This is not a prank. I'm sorry if it seams like it.)
Thanks for any help!
What you can do is use an ObservableCollection here so that the code that is iterating over the collection has a way of detecting when and how the collection is mutated while it is iterating. By using an ObservableCollection the iterating code can increment the index when an item is added before the current index, or decriment it when an item is removed from before the current index.
public static IEnumerable<T> IterateWhileMutating<T>(
this ObservableCollection<T> list)
{
int i = 0;
NotifyCollectionChangedEventHandler handler = (_, args) =>
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
if (args.NewStartingIndex <= i)
i++;
break;
case NotifyCollectionChangedAction.Move:
if (args.NewStartingIndex <= i)
i++;
if (args.OldStartingIndex <= i) //note *not* else if
i--;
break;
case NotifyCollectionChangedAction.Remove:
if (args.OldStartingIndex <= i)
i--;
break;
case NotifyCollectionChangedAction.Reset:
i = int.MaxValue;//end the sequence
break;
default:
//do nothing
break;
}
};
try
{
list.CollectionChanged += handler;
for (i = 0; i < list.Count; i++)
{
yield return list[i];
}
}
finally
{
list.CollectionChanged -= handler;
}
}
The code is taken from this other answer of mine. It contains additional tangential information about the consequences of iterating a sequence while mutating it, as well as some additional explanation about this code and the implications of its design decisions.
I have the idea of marking the objects as removed / inactive.
Yes, I think something like this is a reasonable approach. What I would do is to first collect all the items to remove and then remove them all at once. In code, it could look something like:
var toRemove = new HashSet<Item>();
foreach (var item in list)
{
toRemove.UnionWith(item.GetItemsToRemove());
}
list.RemoveAll(item => toRemove.Contains(item));
The nice thing about this approach is that it should be fast (O(n)), because while removing a single item from a List<T> is O(n), removing multiple items from it at the same time is also O(n).

Simple(?) logic concerning HashSet

I have a HashSet filled with about 50 posts which I want to pair in two by two into my database (the posts are a title and a description that belong together). The problem is that I cant get the logic together. This code below maybe explains a little better what I am thinking of:
foreach(string item in hash)
{
// Here something that assigns every uneven HashSet-post to item1, the even ones to item2
var NewsItem = new News
{
NewsTitle = item1
NewsDescription = item2
};
dbContext db = new dbContext();
db.News.Add(NewsItem);
db.SaveChanges();
}
You cannot "pair up" items from hash-based containers, because from the logical standpoint these containers are ordered arbitrarily *.
Therefore, you need to pair up the titles and descriptions when you insert your data into hash sets, like this:
class Message {
public string Title {get;set;}
public string Description {get;set;}
public int GetHashCode() {return 31*Title.GetHashCode()+Description.GetHashCode();}
public bool Equals(object other) {
if (other == this) return true;
Message obj = other as Message;
if (obj == null) return false;
return Title.Equals(obj.Title) && Description.Equals(obj.Description);
}
}
ISet<Message> hash = new HashSet<Message>();
At this point you can insert messages into your hash set. The titles and descriptions will be always paired up explicitly by participating in a single Message object.
* The current implementation from Microsoft does maintain the insertion order, but this is an unfortunate implementation detail.
I define the first item in the HashSet is odd(1), and the second even(2), etc.
Then a HashSet is not the right data structure. HastSets are not in any particular order, so if you need to extract the items sequentially then a plain List<string> would work.
That said, one way to do what you need is to use a for loop that gets items two-at-a-
time:
using(dbContext db = new dbContext())
{
for(int i = 0; i < list.Count - 1; i += 2)
{
var NewsItem = new News
{
NewsTitle = list[i];
NewsDescription = list[i+1];
};
db.News.Add(NewsItem);
}
}
db.SaveChanges();

Categories