Access Parent/Owning class variable from composed class? - c#

Forgive me because I know my wording is terrible. I'll just give an example.
public class MainClass{
public int someVariable;
public List<HasAClass> cList = new List<HasAClass>();
addHasAClass(HasAClass c){
cList.Add(c);
}
}
public class HasAClass{
public HasAClass(){
//Modify someVariable in some way????
}
}
public class HasASubClass : HasAClass{
public ComposedClass(){
//Modify someVariable in some way???
}
}
I having trouble finding the right words for this questions but here is what I am trying to do:
I am creating an aid for an RPG similar to dungeons and dragons. Each character can have a variety of special abilitys which can effect the characters in some way (both negative and positive). I am trying do this with a variety of subclasses which store the pertinent info and get added to the character at varying points in time. What I can't figure out is how to modify the properties of the Character(I called it Main Class in my example) when instances of the HasA class are added to it.

The HasAClass needs a reference to the owning instance, so that it can ask the parent for values and update them when required...
public class HasAClass
{
private MainClass _mainClass;
public HasAClass(MainClass mainClass)
{
_mainClass = mainClass;
_mainClass.someVaraible = 42;
}
}
You then need to pass the owner reference into the constructor of the HasAClass when they are created. If this is not possible at the time of creating the instance then you would instead need to assign it as a property after it has been created. Such as inside the addHasAClass method.

Related

C# Access to subclass Proprieties from base class instance

I don't really know how to formulate my issue it's a bit complicated for me, i'll try my best to explain.
I'm making a space game, i have a base class which represent places, and i want to have different type of places like planets, space stations, asteroïds, trading ships etc. The player can click on those objects and get informations.
So my classes looks like something like this:
public class Place {
public int placeId;
public string placeName;
public string placeDescription;
/* Place constructor */
}
public class Planet : Place {
/* Specific proprieties of planet */
public PlanetType planetType;
public int planetSize;
...
// Planet constructor
public Planet(int placeId, string placeName, string placeDescription, PlanetType planetType, int planetSize) : base(placeId, placeName, placeDescription) {
this.planetType = planetType;
this.planetSize = planetSize;
...
}
}
And i have a delegate which accept a function like selectPlace with Place in parameters because i don't want to make a delegate for each type of Place i have.
In another script which is supposed to show the information of any kind of Place, i recieves the Place object that the player clicked on. I think i found a solution, however is this correct to do something like this ?
private void updateSelectedPlaceUI(object sender, EventsController.PlaceEventArgs placeArgs){
// This is just a test, i should check which type of subclass it is before
Planet planetTest = placeArgs.Place as Planet; // So now i can use planetTest.planetType
}
And placing this in a switch case so i can handle any type. I just want to be able to get the proprieties from any derived class of Place in order to display them in UI. I would like to know a better way to achieve this.
But i'm wondering if my design is ok and necessary, it has been a while since i haven't used inheritance / polymorphism, and i feel like i'm doing it the wrong way.
I would propably make the UI part of showing the properties a specific place generic to accept something like a PropertyItem, you can decide the properties yourself.
public class PropertyItem
{
public string Text { get; set; }
public object Value { get; set; }
}
And then in your select method you would just call the abstract method of your base class (make your base class abstract as well)
public abstract class Place
{
...
public abstract IEnumerable<PropertyItem> GetProperties();
}
And now you can override this in your Planet
public class Planet : Place
{
...
public override IEnumerable<PropertyItem> GetProperties()
{
yield return new PropertyItem { Text = "Size", Value = this.planetSize };
}
}
And eventually you would use the GetProperties() method to get the properties of your place and show them in a tabular or what ever format your UI knows how to handle the PropertyItem type.
private void updateSelectedPlaceUI(object sender, EventsController.PlaceEventArgs placeArgs)
{
MyUserInterfaceWidget.DisplayProperties(placeArgs.Place.GetProperties());
}

C# Give an constructor internal value

I am trying to give a class an object which don’t have control over it. That mean if the main class change the object, the class I’ve created have also the changes.
Example:
class main
{
private string test;
public main()
{
var Test = new Test(test);
}
}
If I change now the string “test” the object Test should also see the change string. Is that possible?
It is possible to do if you instead of string use a specially crafted class:
public class SharedData
{
public string Test {get;set;}
}
Then if you have an object of type SharedData instead of string, they will share the value. Strings are immutable in C#, so you wont' have the same string reference in both classes.
class main
{
private SharedData test = new ShareData();
public main()
{
var Test = new Test(test);
}
}
P.S. It's a different question whether this is a good design or not. It's hard to answer based on the examples you have provided. I would avoid such design if possible and rather pass string as parameter where you need it to have less state. But as always it depends and there can be cases where what you do is beneficial, but you can consider changing the design to make it easier.

Accessing a property in one ViewModel from another

I want main viewmodel to have a certain list, and then access from many other viewmodels.
For example, in MainViewModel.cs I will have a list of 50 numbers,
then in NumListViewModel.cs, I'd like to access it in order to show it as a list, and in AddNumViewModel.cs I'd like to be able to update that list.
It's been suggested that I use events / evenaggerator, which I did, but unfortunately, for all I know all I can do with it is send a num from one view to another and tell it to update the list, but the problem is, as the program grows, I will need to have a lot of subscribers in the main view model, and when something actually happens I will have to "publish" events according to the number of subscribers which makes it even harder to maintain.
I also found another answer, instructing to create an instance of anotherVM within the mainVM, with a parameter set to "this" which is a reference to the mainVM.
It works, but then again, it could get quite long.
So my question is, is there a better way to access a property from another VM?
Like literally have the an instance of the class that holds the list in the mainVM, and then just be able to update / access it from the other VMs, without having to explicitly program which VM can. Would make life so much easier.
In your answer, please try to avoid suggesting frameworks.
Although there are some really good ones, I want to be able to do at least that by myself.
For example:
MainVM.cs:
public class MainVM
{
List lst = new List(); //Let's just say it's full...
}
OtherVM.cs:
public class OtherVM
{
lst.Add(3);
}
PS: Yes I know it has been asked already, and yes I have done my research, BUT I the answers I found are too 'static', I guess?
If you want direct access to the list from an external ViewModel, then your options are to:
Pass the List to the OtherVM as a constructor argument or public property. Then the OtherVM can treat it like a member.
Pass the MainVM to the OtherVM as a constructor argument or public property. Then the OtherVM can access the List by first accessing the MainVM.
Example:
public class MainVM
{
public List<XX> MyList { get; set; }
}
public class OtherVM
{
public MainVM TheMainVM { get; set; }
public OtherVM(MainVM theMainVM)
{
TheMainVM = theMainVM;
// Access the MainVM's list
TheMainVM.MyList.Add(stuff);
}
}
Give the MainVM a static property called "Default" or "Instance," so you can access the static instance of MainVM from within OtherVM, without assigning it as a member field.
Example:
public class MainVM
{
private static MainVM _instance = new MainVM();
public static MainVM Instance { get { return _instance; } }
public List<XX> MyList { get; set; }
//other stuff here
}
//From within OtherVM:
MainVM.Instance.MyList.Add(stuff);

Static / Shared variable across certain instances of a class but not others

This may just be me showing my lack of knowledge / bad programming practice, but i'm curious to know if:
a) This already exists
b) If it doesn't exist, if it's bad programming practice to do so
But here's my question:
Suppose I have a class, let's call it "Computer" and it holds data of all the computers in a company. Now, it just so happens that this company has thousands of Dell computers and thousands of HPs and nothing else. (Again please stick with me here, this is just an example to illustrate my point)
Now, I could define my class as follows:
Public Class Computer
Dim Type as string
Dim SerialNumber as string
Dim User as String
...
End Class
Now, in my code I create two lists:
Dim DellComps as new list(of computer)
Dim HPComps as new list(of computer)
Obviously, for the DellComps, all them will have .Type = "Dell" and for the HPComps, all will have .Type = "HP"
Now, I know I could set this variable in the constructor very easily, but I'm wondering if there is a smarter way to declare the variable inside the class - Similar to the VB Shared / C# Static statement where all the instances of the class share the same variable.
My thoughts are:
Inherit the class and create a shared variable in the child class
Just leave it as is and declare the Type var in the constructor
Maybe this is something that could be done via interfaces somehow
MOST PROBABLE - something i just don't know about
Thanks and I hope what I'm asking makes sense!!!
The closest thing you'd have is done with the abstract keyword. You would have an abstract class Computer, that would then be overridden by the concrete subclasses DellComputer and HpComputer. A crude (C#) example would be:
public abstract class Computer
{
public string Type { get; protected set; }
}
public class DellComputer : Computer
{
public DellComputer()
{
this.Type = "Dell"
}
}
You generally don't want to share a single variable among a ton of instances because that breaks encapsulation, and more realistically can become a big problem when attempting to unit test code. So the pure form of what you're talking about isn't a terribly good idea, but the realistic use case is pretty common, and definitely supported.
EDIT: As part of the comments below, here's a different approach that uses the very closely related virtual keyword!
public abstract class Computer
{
public virtual string Type { get; }
}
public class DellComputer : Computer
{
public override string Type
{
get {
return "Dell";
}
}
}
If you are always setting a flag in the constructor indicating the type of computer (which is NOT a typical business object scenario, where the type can be edited), chances are that you can really solve your problem using subclasses.
Subclass Computer to create DellComputer and HpComputer classes.
When creating lists of each type of computer, one approach is to have a master list of all computers and use Linq's Enumerable.OfType(TResult) to select instances that match the type you are interested in.
If indeed you want the type of class to be editable after the class is created, instead provide a property to modify the type of computer. You may for convenience provide a constructor overload that also sets the property (though I would shy away from that personally). If you do, have that constructor overload use the property to set the type.
UPDATE
Example of what the factory pattern might look like.
public abstract class Computer
{
public virtual string Type { get; }
}
public class DellComputer : Computer
{
public override string Type
{
get { return "Dell"; }
}
}
public class HpComputer : Computer
{
public override string Type
{
get { return "HP"; }
}
}
// Here I'm using an enum to indicate the type desired. You might use a string
// or anything else that makes sense in your problem domain.
public enum ComputerType
{
Dell = 1,
Hp = 2
}
public class ComputerFactory
{
public Computer Create(ComputerType type)
{
switch (type)
{
case ComputerType.Dell:
return new DellComputer();
case ComputerType.Hp:
return new HpComputer();
default:
throw new InvalidArgumentException();
}
}
}
// Usage would be something like:
List<Computer> computers = new List<Computer>();
computers.Add(ComputerFactory.Create(ComputerTypes.Dell);
computers.Add(ComputerFactory.Create(ComputerTypes.Dell);
computers.Add(ComputerFactory.Create(ComputerTypes.Hp);
You could create a class that has a collection and other data
In this case PC would not have a type.
public class Computers
{
private List<Computer> pcs= new List<computer>();
public List<Computer> PCs get { return { pcs; } };
public String Brand { get; private set; }
public Computers(string brand) {Brand = brand;}
}
Regarding a static variable. You don't want all members of the class share Brand.
With the said just repeat the data in the constructor.
If a Dell has the same Properties as an HP then I would use the same class.
If you stated buying a new brand do you really want to create a new class or subclass?
If you want a structured list of brands then I would use and Enum rather than a separate class for each brand.

class variable gets reset when calling methods in multiple forms

Updated to reflect to my own source
I'm in process of building my first winform application in c# and I'm trying to figure out the best practice for structuring my classes to work smoothly when I use them in my forms.
I have a couple of examples which I will try to explain the best way i can.
When working with get/set variables in a class, the best practice should be something like this:
JobMove.cs
public class JobMove
{
private List<string> jobNames { get; set; }
public string Scanner;
public JobMove()
{
this.Scanner = Properties.Settings.Default.Scanner;
}
public void ListSelected(ListBox lbx)
{
foreach (string jName in this.jobNames)
{
lbx.Items.Add(jName);
}
}
public static List<string> GetCheckedJobs(ListView lw)
{
int countChecked = lw.CheckedItems.Count;
int itemCount = 0;
List<string> jList = new List<string>();
foreach (ListViewItem item in lw.CheckedItems)
{
JobInfo jobInfo = Job.Find(Convert.ToInt32(lw.Items[item.Index].SubItems[1].Text));
jList.Add(jobInfo.Name);
itemCount++;
}
return jList;
}
}
My problem is when I combine this with my forms and I call this, then I would try to do something like this:
MyForm1.cs
public partial class MyForm1 : Form
{
private void btnMoveJobs_Click(object sender, EventArgs e)
{
Properties.Settings.Default.Scanner = cbxScanners.SelectedItem.ToString();
JobMove moveJobs = new JobMove();
frmMoveJobs FrmMoveJobs = new frmMoveJobs();
FrmMoveJobs.ShowDialog();
}
}
MyForm2.cs
public partial class frmMoveJobs : Form
{
public frmMoveJobs()
{
InitializeComponent();
JobMove moveJobs = new JobMove();
lblFrom.Text = moveJobs.Scanner;
moveJobs.ListSelected(lbxJobsToMove);
cbxMjScanners.DataSource = System.Enum.GetValues(typeof(Scanners));
}
}
But when I call MyClass in MyForm2 and I want to call the DoSomethingElse method, then myString will be reset to a null value. And that makes sense to me, but how do I work around this?
I tried to figure out what to use here to get easier around these flaws in my code, but my knowledge is far too weak to just implement an easy solution.
I know I could just store this variable in Settings.settings as an example, but to me that just seems like a real overload for such a simple task.
I might just need a point in the right direction to right on what to do in this situation.
If you do a MyClass myClass = new MyClass(); then indeed - the values are independent and unrelated. If you want to share the MyClass instance then pass the MyClass instance between the forms. Perhaps:
using(var form2 = new Form2()) {
form2.SensibleName = existingMyClassInstance;
form2.ShowDialog();
}
(note the using above btw; when using ShowDialog() it is your job to make sure the form is disposed; it only gets disposed automatically if using Show())
Firstly, they're properties, not variables (the variables are the underlying data source).
Secondly, the whole point of get/set accessors is so you can get and set the value without needing helper methods.
Thirdly, and as to your problem, you're creating a new instance of the class in each form (hinted at by the new keyword) and the value of the property will be whatever it is initialised as on construction of the instance (or not.) i.e. the values of properties are not shared between different instances of the same type.
Think of the mold for a key: I can get multiple instances of the key cut from a "blueprint", but any damage that one suffers won't be reflected by the rest - they're unique in that sense.
If you want the forms to both access the same instance of that type, then you will need to stash the instance somewhere in your code which is accessible to both.
A few options:
Pass in an instance of MyClass in the form2's constructor.
Make MyClass a static property of either Form1 or Form2 and access it via that on the other form.
Make MyClass static (not recommended).
If you want to use the instance of MyClass created in MyForm1 inside of MyForm2, you need to provide it to MyForm2.
Something like this would work:
public partial class MyForm2 : Form
{
public MyForm2(MyClass given)
{
InitializeComponent();
given.DoSomethingElse();
}
}
Easy Solution:
private static string myString { get; set; }
Why: because you initialize the class again when initializing Form2 and it will create a new class. With the "static" keyword you create a property which is the same in all instances of this class.
BUT: please read some books before continuing, this would be the solution to this problem, but the source of many others. Try to understand C# and Forms first, than (or alongside with reading/learning) start coding!
this is because each of your form has a new object of "MyClass".
To achieve what you want to do use a static property... this won't be initialized and gives back the same value for each object of MyClass
it looks like this
public class MyClass {
public static string myString { get; set; }
public void ChangeMyString(string newString)
{
myString = newString;
}
public void DoSomethingElse()
{
MessageBox.Show(myString);
}
}

Categories