C# Getting access to subclass variable while operating on superclass objects - c#

I am building a simple german learning helper program and one of its elements is a dictionary. To manage all the words, I've added three classes: Noun, Adjective and Verb. They all inherit from another abstract class Word. The noun class also contains an Article enum.
All the words (Adjective, Noun and Verb objects) are stored in a List<Word>.. I have trouble printing them in ListView columns or more specifically - getting access to the field I want.
Basically, I want to loop through all the words in the List. While doing this, check if current item is a noun (Noun type of object) - if so, get its article. If not, print "--" and then add the word translations to columns.
Here is the method:
private void UpdateList(List<Word> currentWordList) //passing the List
{
wordsListView.Items.Clear();
int index = 1; // first column of the number
foreach (Word w in currentWordList)
{
ListViewItem newItem = new ListViewItem("" + index);
if (w is Noun)
newItem.SubItems.Add(w.Article); //here is the error (no definition for "Article")
else
newItem.SubItems.Add("--");
newItem.SubItems.Add(w.GermanTranslation);
newItem.SubItems.Add(w.PolishTranslation);
wordsListView.Items.Add(newItem);
}
}
How do I solve this?
Thank you very much for your help.

You could cast w to Noun like this:
Noun noun = (Noun)w;
Then use the noun variable to access the Article.
Having said that, a better way to do it is to define an abstract method in Word called something like GetDescription that gets the description of the word.
Here is an example:
public abstract class Word
{
public abstract string GetDescription();
//....
}
public class Noun : Word
{
public override string GetDescription()
{
//Here you can access the Article property
//...
}
//...
}
Then in the loop, you can use the GetDescription method to get the description of each word.

You need to cast W to the Child Class. Like so
newItem.SubItems.Add(((Noun)w).Article)

Related

Is there a way of storing an object's position in a C# List INSIDE the object?

It should be noted that it's possible for each objects' fields to be indistinguishable from one another within the list and the best way to differentiate them is by ID. It would be extremely helpful if an object's ID could also be its Index in that list.
//...
receiveData(ClassA obj2Add) {
maplist.Insert(0, obj2Add); //Defined as a Maplist, which extends List<ClassA>, assume with no other fields of relevance
Console.WriteLine(obj2Add.Index); //Should display zero.
}
class ClassA {
string mapname {get;set;}
int Index; //Would reference this ClassA's position in maplist.
}
Is there anything I can add to ClassA or the Maplist class to fulfill this goal?
It is possible only if your ClassA ID starts from 0 and then you can do something like:
receiveData(ClassA obj2Add) {
maplist.Insert(0, obj2Add);
maplist=maplist.OrderBy(o=> o.Index).ToList();
Console.WriteLine(obj2Add.Index);
}
Why do you need that anyway? I would guess that you want to select the element by maplist[id]
If this is the case, then I would suggest using
maplis.FirstOrDefault(f=>f.Index==id);

How to sort a List<> that contains derived class objects, by derived class method

I have a double problem here. I need to sort a List<> that I know contains objects of a derived class to the class that the list was declared to contain originally. AND, I need to sort by the return value from a method in that derived class, which takes a parameter. Keep in mind that I already know the List contains objects all of the derived class type.
I've created some sample code here to demonstrate the question since the real code cannot be shared publicly. Note, I have no control over the base conditions here (i.e. the fact that the List<> collection's declared contents are the parent class and that it contains objects of the derived class, which contains a method that takes an argument and returns the values that I need to sort the collection by). So, I doubt I'd be able to use any suggestion that requires changes there. What I think I need is a way to specify (cast?) what is really in the List so I can access the method defined there. But I'm open to other thoughts for sure. Otherwise I'm left with a traditional bubble sort. Thanks.
public class Component
{
public int X;
public int Y;
}
public class ComponentList : List<Component>
{
// Other members that deal with Components, generically
}
public class Fence : Component
{
public int Distance(int FromX, int FromY)
{
int returnValue = 0;
// Caluclate distance...
return returnValue;
}
}
public class Yard : Component
{
// Yada yada yada
}
public class MyCode
{
public List<Component> MyFences;
public MyCode(List<Component> Fences, int FromX, int FromY)
{
// Sort the fences by their distance from specified X,Y
Fences.Sort((A as Fence, B as Fence) => A.Distance(FromX, FromY).CompareTo(B.Distance(FromX, FromY)));
// Or
List<Fence> sortedFences = MyFences.OrderBy(A => A.Distance(FromX, FromY)).ToList();
// Or ???
}
}
Use the Enumerable.Cast<Fence> extension method to transform your IEnumerable<Component> to IEnumerable<Fence>. Then I'd use your second approach (the OrderBy approach) to sort it, but that's my preference.
List<Fence> sortedFences = MyFences.Cast<Fence>().OrderBy(A => A.Distance(FromX, FromY)).ToList();
This approach will throw if there is an object in MyFences that can't be cast to Fence. If you expect that the code should only be passed Fences, this might be what you want. If, instead, you want to skip over non-Fence members, you can use:
List<Fence> sortedFences = MyFences.OfType<Fence>().OrderBy(A => A.Distance(FromX, FromY)).ToList();

Matching specific entries in lists

Currently I'm trying to write a CSV exporter utility for work to help speed up the process of creating work orders for our supervisors. On paper it's quite a simple concept, import the CSV containing our part information and our current production requirements. The columns of the CSV are being split into separate lists. I'm trying to create a button that will allow the user to automatically set the quantity based on the part number.
The way I'm currently thinking about doing this is by grabbing the specific entry on the list's 'spot' (can't think of a better term).
Example:
Dinosaur List
1. T-Rex
2. Triceratops
3. Allosaurus
Diet List
1. Carnivore
2. Herbivore
3. Carnivore
If my user selected Allosaurus, I would want a value returned of 3, and then I would use that to grab the right entry from my second list, in this case, Carnivore.
I'm not sure exactly how I would go about doing this, and any help or direction would be greatly appreciated.
You should really use Object-Oriented programming in this case.
If I were you, I'd declare a class Dinosaur, and make subclasses for each type of dinosaur. In the super class (Dinosaur), put an abstract property of type DinosaurDiet to force subclasses to implement this property. Here's a bit of code to explain what I'm saying:
enum DinosaurDiet //The enumeration for the types of diet
{
Carnivore,
Herbivore
}
abstract class Dinosaur //abstract meaning it can't be instanciated, and only serves as a superclass
{
public abstract DinosaurDiet Diet { get; }
}
class TRex : Dinosaur
{
public override DinosaurDiet Diet { get { return DinosaurDiet.Carnivore; } }
}
class Triceratop : Dinosaur
{
public override DinosaurDiet Diet { get { return DinosaurDiet.Herbivore; } }
}
class Allosaurus : Dinosaur
{
public override DinosaurDiet Diet { get { return DinosaurDiet.Carnivore; } }
}
Once you have all that, you can make a list of them and get them using an index. Here's how:
List<Dinosaur> dinos = new List<Dinosaur>();
dinos.Add(new TRex());
dinos.Add(new Triceratop());
dinos.Add(new Allosaurus());
//Get the 2nd dinosaur from the list (0-based)
int index = 1;
Dinosaur d = dinos[index];
Make sure to test index >= 0 && index < dinos.Count to avoid an exception when trying to get an element at an out-of-bound index.

Taking an object and adding it to the appropritae collection

I am doing a few labs and it has me creating multiple classes that are inheriting from a base class. i have created the base class, a student class that inherits from the base class, which creates a student and a teacher class that inherits from the base class, which creates a teacher. Now i am working on creating a school class that does not inherit any class. i have done most of what it is wanting me to do, but i am stuck on creating the appropriate methods to add the objects to its appropriate classes. I need assistance and guidance on how to create these methods so i may proceed. I am just going to post the Student class that i am working in right now and the instructions. I am not looking for someone to do my homework for me, i just cant seem to find anything online that can guide me in the right directions. thank you for your help.
Methods
Add(base) - Takes a teacher/student object and adds it to the
appropriate collection.
Print(base[]) - Private method that takes an
array of your base class object and prints all the elements of the
array.
3 Print(bool students = true) - Public method that prints out the
list of students, or list of teachers based upon the parameter value.
This is done by calling the Print(base[]) with the student[] or
teacher[] based upon the bool.
namespace BaseClass
{
class School
{
List<Teacher> staff = new List<Teacher>();
List<Student> students = new List<Student>();
public Student Students
{
get
{
students.Count();
return Students;
}
}
public Teacher Staff
{
get
{
if(Staff.EnumProp == Status.Employeed)
{
staff.Count();
}
return Staff;
}
}
public void Add(Teacher t1, Student s1) //not sure if this is correct or
//what to do in this method??
{
staff.Add(t1);
students.Add(s1);
//i also need help in the following methods. i am not sure what needs to be put
//in the parameter of the method, based on the instructions.
Based on requirement 1, you'd have to Add(Base c) and determine the collection to add to from there. e.g. (c is Teacher ? staff : students).Add(c). Normally such a class would have overloads (Add(Teacher) and Add(Student)) separately as well to be able to add directly.
public void Add(Base c)
{
if(c is Teacher)
staff.Add((Teacher)c);
else
students.Add((Student)c);
}
2 depends on the output type. With assignments outputting to the console is often enough, so you can use something like
void Print(params Base[] peeps)
{
foreach(var c in peeps)
c.Print();
}
No matter how Print is implemented, step 3 is actually very easy. You can just call your Print(Base[]) as stated in the requirements. To get that array, you have to determine which collection to use, just as in req. 1. (it does sound like 2 separate collections are wanted, otherwise a single collection could be used where Base exposes the role of the person).
public void Print(bool students = true)
{
if(students)
Print(this.students.ToArray());
else
Print(staff.ToArray());
}
PS, as mentioned in the comments the Students and Staff properties seem to expose some behavior that could be changed, but since that outside the scope of the question, won't go there unless you want us to ;)
Add(base) - Takes a teacher/student object and adds it to the appropriate collection.
For adding you can have two methods with same name but different signature. they are called method overloads. Compiler can distinguish between them by looking at the parameters they take.
These methods are both defined in base method. but better design would be to put each method in its appropriate class. (i.e Teacher and Student class)
public void Add(Teacher teacher)
{
staff.Add(teacher);
}
public void Add(Student student)
{
students.Add(student);
}
Print(base[]) - Private method that takes an array of your base class object and prints all the elements of the array.
If i understood correctly you want to print all elements of student or teacher. thats all?
In your base class you can have private method that prints array.
private void Print(Base[] array)
{
for (int i = 0; i < array.Length; i++)
{
Console.WriteLine(array[i].ToString());
}
}
It is better to override ToString method for both Student and Teacher class. for example this method is required in both classes.
public override string ToString() // write this method in both student and teacher classes.
{
return string.Format("Name : {0} , Age : {1}",studentName,studentAge ); // return optional information of student instance.
}
3 Print(bool students = true) - Public method that prints out the list of students, or list of teachers based upon the parameter value. This is done by calling the Print(base[]) with the student[] or teacher[] based upon the bool.
You just need a simple check.
public void Print(bool students = true)
{
if(students)
Print(Students.ToArray());
else
Print(Staff.ToArray());
}
This only works if its inside Base class behind Print(base[]). Otherwise Print(base[]) have to be protected.
A better design would be to add each print method in child classes separately.
In your get and set method what you are doing is really useless.
Count() is a linq method that counts and gives you the length of list. you can use the property of list itself. Count(without parenthesis) which directly gives you the length of list.
Also you dont store the result anywhere so thats why i said its useless. You may want to store the total count. then you can do this.
public int TotalCount
{
get { return staff.Count + students.Count; }
}

Accessing fields in a derived class

I'm creating a list of nodes on the fly through a user interface. In my List I can add any number of objects (AAA, BBB, etc) to the List based on the class structure below by instantiating these objects through reflection.
public abstract class Node : IDisposable
{
protected int x;
}
public class AAA : Node
{
public int iA;
}
public class BBB : Node
{
public int iB;
}
After creating the List I want to access the the extended fields in the derived objects. I know that I have to downcast to access the extended fields but in order to do that presently I have to perform an explicit cast.
foreach (Node nn in MyList) //assume the first node in the list is AAA
{
int m = ((namespace.AAA) nn).iA; //this works
int n = (AAA) nn).iA; //this works
}
I was wondering if I can use a string to create the actual downcast. Maybe it can't be done. Maybe I'm missing something. What I would like to do which DOESN'T work would be something like the following.
foreach (Node nn in MyList) //assume the first node in the list is AAA
{
Type t2 = nn.GetType(); //{Name = AAA; FullName = namespace.AAA} (*debugger*)
string str = t2.FullName; //namespace.AAA
int m = ((str) nn).iA; //this DOESN'T work
}
When I look at the value of nn in the debugger the FullName represents the Class I want to use for the downcast.
I could get around this by using a switch statement based on the string representing the class and hard code in the cast statement but because I have over 100 different nodes and I will be adding more nodes in the future, I would have to modify the switch statement every time a node is added. This is something I would prefer not to do if all possible.
Thanks in advance for any response.
Thanks to Douglas for pointing out that I could use FieldInfo to get the value of iA for example. I just wanted to expand a little more on this topic. If I wanted to take Class AAA and extend it through composition would I also be able to access the fields in those classes through the FieldInfo.
public class AAA : Node
{
public int iA;
public X[] XArray; //where X is some other random class with pubic fields
public Y[] YArray; //where Y is some other abstract class
}
What you are doing seems like a product of a faulty idea somewhere in your design.
Instead of having iA and iB in your classes, why doesn't the base node have a property called iNodeImplementation which you set in the constructor of AAA or BBB? Then you don't have to do all this fancy casting.
I have a feeling your trying to be too cute and missed some basic OOD principles. Consider how you can refactor your classes to make your code simpler.
Without going into the merits of whether this should be done, here's some sample code showing how it can be done using reflection:
Type aaaType = Type.GetType("namespace.AAA");
FieldInfo iAField = aaaType.GetField("iA", BindingFlags.Public |
BindingFlags.Instance);
int m = (int)iAField.GetValue(nn);
Why you don't try to set abstract properties in Node class and than implement in AAA and BBB.
public abstract class Node : IDisposable
{
protected int x;
public abstract int i;
}
public class AAA : Node
{
public override int i;
}
public class BBB : Node
{
public override int i;
}
And than use the foreach like this:
foreach (Node nn in MyList)
{
int m = nn.i;
}
To me, this is a problem in your object design, not in C#.
You've got a lot of nodes that you want to treat generically (great) but you want to be able to get specialized data from them in unique ways. This is not so great in the context of having a (possibly) unbounded amount of unique data.
The problem is that you want the best of both worlds: unique encapsulated data and free generic access to that data.
I would say that you need to take your Node design and think long and hard what kind of operations/accessing should be available to generic consumers of the nodes and provide that in an abstract base class or a small(ish) number of interfaces that provide that access.
Otherwise, you're looking at a lot of down-casting somewhere in your code or work with reflection to best guess things or a standard interface for describing and getting the values you want.

Categories