Static predefined constant objects in a class - c#

I am working on a C# game that will have predefined levels. I am trying to have a class that will hold the predefined data of all of the levels. Here's what I'm trying to do:
public static GameLevel startLevel = new Level() {
startLevel.Actions.Add(action);
startLevel.Actions.Add(action);
}
And so on. However, it seems that C# does not want me to initialize this way. How can I achieve my desired effect without throwing it into a massive constructor?

How do you think if we change the static variable as below:
private static GameLevel _startLevel;
public static GameLevel StartLevel
{
get
{
if(_startLevel == null)
{
_startLevel = new Level();
_startLevel.Action.Add(action1);
_startLevel.Action.Add(action2);
}
return _startLevel;
}
}

Since you have predefined levels, I suggest a little different approach.
Create a Level base class, and a class for each Level. The constructor for each level class can set up the Actions and any other things the game needs to know how to display itself.
using System;
public class Program
{
public static void Main()
{
new GameState(new Level1());
Console.WriteLine("Current level is " + GameState.CurrentLevel.Name);
Console.WriteLine("User leveled up");
GameState.CurrentLevel = new Level2();
Console.WriteLine("Current level is " + GameState.CurrentLevel.Name);
}
}
public class Level
{
public string Name;
// public static IEnumerable<Action> Actions { get; set; }
}
public class Level1 : Level
{
public Level1()
{
// level 1 init
Name = "1";
// Actions = new List<Action> { ... }
}
}
public class Level2 : Level
{
public Level2()
{
// level 2 init
Name = "2";
}
}
public class GameState
{
public static Level CurrentLevel { get; set; }
public GameState(Level startLevel)
{
CurrentLevel = startLevel;
}
}
Working copy: https://dotnetfiddle.net/qMxUbw

"...C# does not want me to initialize this way..."
You can init this way. You simply don't have the right syntax. This should work
public static Level startLevel = new Level()
{
Actions = new List<Action>()
{
new Action() {...},
new Action() {...}
},
OtherProprty = "Other"
};
NOTE: this has to be done under class scope
"Massive constructor" - you usually don't init static members in constructor unless this is static constructor. Sounds like you need to use Singleton pattern for this piece. Then again, you call all the needed code in constructor, "massive" or not. Break it into methods.

Related

How can i Access a private variable in another class in C#

I wrote the code below and i want to access the private varibale in another class, i created instance of the class and tried to access it but couldn't. can someone point out what I did wrong in the code below?
using System;
namespace lab_first
{
public class AccessModifiers
{
private int Abc { get; set; }
private int bcd { get; set; }
}
class Program
{
static void Main(string[] args)
{
var acc = new AccessModifiers();
Console.WriteLine(acc.Abc)
}
}
}
You make members private so that nobody outside the class can access them.
This goes inline with the principle of information hiding.
Your example should look like this:
public class AccessModifiers
{
// You can only access this inside of the class AccessModifiers
private int Abc { get; set; }
internal void SetValue(int x){
// Access possible, because SetValue() is inside the same class
Abc = x;
}
internal int GetValue(){
// Access possible, because GetValue() is inside the same class
return Abc;
}
}
class Program
{
static void Main(string[] args)
{
var acc = new AccessModifiers();
// Abc is never modified directly, only indirectly.
acc.SetValue(5);
Console.WriteLine(acc.GetValue());
}
}
However, there is still a way to access the private member. It's called Reflection. However, note that private variables are considered an implementation detail and might change at any time, so you can't rely on it. E.g. someone might change the name from Abc to def and your Reflection-based approach fails.
You can either change private to internal or public in this case.
Another way is declaring the variables in the class as private and using C# Properties in the class to set and get the values of variables. this is called encapsulation which is a protective shield that prevents the data from being accessed by the code outside this shield).
public class AccessModifiers
{
private int _abc { get; set; }
private int _bcd { get; set; }
public int Abc
{
get
{
return _abc;
}
set
{
_abc = value;
}
}
public int Bcd
{
get
{
return _bcd;
}
set
{
_bcd = value;
}
}
}

C# Accessing Class Data, Beginner Question

I am trying to make a database of weapons and have multiple attributes assigned to each weapon, such as name, attack value, buy value, sell value, etc.
namespace Testing
{
public class Weapon
{
public string name;
//other attributes would go here too
}
class Program
{
static void WeaponBuilder()
{
Weapon bSword = new Weapon();
bSword.name = "Bronze Sword";
//many other weapons would be built here
Console.WriteLine(bSword.name); //works fine
}
static void Main(string[] args)
{
WeaponBuilder();
Console.WriteLine(bSword.name); //error: bSword does not exist in the current context
WeaponShop();
Console.ReadLine();
}
static void WeaponShop()
{
Console.WriteLine("Buy " + bSword.name + "?"); //error: bSword does not exist in the current context
}
}
}
I need to be able to access the weapon's data outside of where it was constructed. Any help is appreciated. I know this is a noob question and I apologize.
You can put the data at the class level instead of an ephemeral local scoped variable:
class Program
{
static public Weapon bSword { get; } = new Weapon();
static void InitializeWeapon()
{
BSword.name = "Bronze Sword";
Console.WriteLine(bSword.name); //works fine
}
static void Main(string[] args)
{
InitializeWeapon();
Console.WriteLine(BSword.name);
WeaponShop();
Console.ReadLine();
}
...
}
Therefore it will be accessible inside the class and from outside in read-only mode here, as a composite.
It seems you want to manage several weapons, so you can for example use a List:
public class Weapon
{
public string Name { get; set; }
}
class Program
{
static public List<Weapon> Weapons { get; } = new List<Weapon>();
static void InitializeWeapons()
{
Weapons.Add(new Weapon { Name = "Bronze Sword" });
Weapons.Add(new Weapon { Name = "Silver Sword" });
foreach ( var weapon in Weapons )
Console.WriteLine(weapon.Name);
}
static void Main(string[] args)
{
InitializeWeapons();
ShopWeapon();
Console.ReadLine();
}
static void ShopWeapon()
{
foreach ( var weapon in Weapons )
{
Console.WriteLine($"Buy {weapon.Name}?");
// ...
}
}
}
What Are OOP Concepts?
What is abstraction in C#?
How to choose between public, private and protected access modifier?
What is polymorphism?
What is the difference between an interface and a class?
A basic structure for your classes can be something like:
// abstract means you cannot directly create a Weapon.
// Is is intended to be a base for concrete weapons
public abstract class Weapon
{
public abstract string Name { get; set; }
}
// A sword is a concrete weapon
// The base class "Weapon" forces it to have a name
public class Sword : Weapon
{
public override string Name { get; set; } = "sword";
}
// our weaponshop with a list of available weapons
public class WeaponShop
{
public List<Weapon> Weapons { get; } = new List<Weapon>();
// Let's add a sword to our weapon list when we are created
public WeaponShop()
{
Weapons.Add(new Sword());
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
WeaponShop myWeaponShop = new WeaponShop();
Console.WriteLine("The first sword is: " + myWeaponShop.Weapons.OfType<Sword>().FirstOrDefault().Name);
}
}
Create your class library on another file, (a good name would be weapon models), Make sure your class is set to static public (you pretty much set your data to static most the time, and public so it is accessible). Now in your Main file declare it on the top with a using statement or start typing the class you want to initiate and press Ctrl period. Then select add a using statement. This will automatically select your library, then you can use it! Also another shortcut, if you type the letters prop then tab twice, it will create a property for your class. Just be sure to initialize it! Also this was done using Visual Studio, some of the shortcuts might not work on VS code.

Attribute to mark as "internal use"

I made a class which requires the public default constructor but
that is never called; instead another constructor is used at DataGrid.AddingNewItem.
I'd like to tell developers that the default constructor is not for their use.
Is there an attribute which suits the purpose?
I had checked DebuggerNonUserCode and MethodImplAttribute with MethodImplAttributes.InternalCall but not sure that's the proper approach.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.dataGrid1.CanUserAddRows = true;
var list = new List<RowX>();
this.dataGrid1.ItemsSource = CollectionViewSource.GetDefaultView(list);
this.dataGrid1.AddingNewItem += (s, e) => e.NewItem = new RowX("ABC");
}
}
public class RowX
{
public RowX()
{
//this is not used. but CollectionView require this to be public or
//CanUserAddRows doesn't work.
}
public RowX(object o)
{
//this is the actual ctor.
}
public string Text { get; set; }
}
Mark it private
class Foo
{
private Foo() {}
}
You can give your constructor an access modifier.
private This means it can only be called from another constructor in that class.
public class PrivateClass
{
//Only from inside this class:
private PrivateClass()
{
}
public static PrivateClass GetPrivateClass()
{
//This calls the private constructor so you can control exactly what happens
return new PrivateClass();
}
}
internal This means only code in the same assembly (i.e. from inside your library) can access it.
public class InternalClass
{
//Only from within the same assembly
internal InternalClass(string foo)
{
}
}

Assign multiple values to one object(?)

Note: I am somewhat of a beginner to C#.
I'm working on a little game that will have a bunch of different levels. Each level has its own class that contains variables (and other irrelevant code). Since I need to pass these values to the main class (my form) I have made them all into methods that returns the value I want (since I can't come up with a better solution). example:
class Level01
{
public int Boxes() { return 3; }
public int MaxPoints() { return 46; }
public int Health() { return 63; }
public int[,] SolidBoxes()
{
int[,] position = new int[Boxes(), Boxes()];
position[1, 1] = 1;
return position;
}
}
When I access these values from my form class I do
int boxes;
int maxPoints;
int health;
int[,] solidBoxes;
void readLevelData() //Starts each new level
{
//Reads the correct level
switch (levelNo)
{
case 1:
setValues(Lvl01.Boxes(), Lvl01.MaxPoints(), Lvl01.Health(), Lvl01.SolidBoxes());
break;
//The same case 2:, 3: for Level02,03..
}
}
void setValues(int getBoxes, int getMaxPoints, int getHealth, int[,] getSolidBoxes)
{
boxes = getBoxes;
maxPoints = getMaxPoints;
health = getHealth;
solidBoxes = getSolidBoxes;
}
I am aware that there's probably a million things in my code here that can be done better and I gladly listen if you have any suggestions, but the thing I wish to ask is:
How can I get all the values from each class using maybe just one name? Ex. Instead doing as I do now, is there a way so I can do something similar to this:
case 1:
setValues(Lvl01.Values);
break;
The problem here is in the setValues method, some of the levels has quite a lot of settings that I wish to use, but I doubt the method would want to take like 15 parameters, and I'm not sure what to do when some levels are not using settings that other levels use.
How should I change my code so I do not have to use every single value as a parameter?
You could use a Dictionary<int, Level> to lookup the object representing each level. Instead of the switch/case, you would do something like
Level level = myLevelDictionary[currentLevel];
That requires you change your classes from having one class per level, to one class that represents any level, e.g.:
class Level
{
public int Boxes { get; set; }
public int MaxPoints { get; set; }
public int Health { get; set; }
public int[,] SolidBoxes()
{
int[,] position = new int[boardSize, boardSize];
position[1, 1] = 1;
return position;
}
}
You would then populate your dictionary like
Dictionary<int, Level> myLevelDictionary = new Dictionary<int, Level>()
{
{ 1, new Level() { Boxes = 3, MaxPoints = 46, Health = 63 } },
// etc.
};
UPDATE
A note about abstract classes
You mention abstract classes in your comments. They are useful if all of your levels have some behavior in common, but some have specialized behavior too. They will often be used in games for things that can move in the game, e.g. something like
abstract class Character
{
// Something everyone has
public int HitPoints { get; set; }
// Something everyone does, but does differently
public abstract void Attack(Character target);
}
public class Fighter : Character
{
public int SwordDamage { get; set; }
public void Attack(Character target)
{
target.Damage(this.SwordDamage - target.PhysicalDamageResistance);
}
}
public class Mage : Character
{
public int MagicFireDamage { get; set; }
public int MagicColdDamage { get; set; }
public void Attack(Character target)
{
if (UseFireDamage()) // e.g. roll random chance that the mage uses a fire spell
{
target.Damage(this.SwordDamage - target.FireDamageResistance);
}
else
{
target.Damage(this.SwordDamage - target.ColdDamageResistance);
}
}
}
one way maybe to use a dictionary.
class Level01
{
Dictionary<string,int> values;
public level01()
{
values.Add("Boxes",3);
values.Add("MaxPoints",3);
values.Add("Health",3);
}
//indexer
public int this[string s] {get{return values[s];} set {values[s] = value;}}
}
and use like:
Level01 lv = new Level01();
somemethod(lv["Boxes"]); //passes 3 to some method
although really you would want to use Dictionary<string,object> and add some type checking and other things to make it work smoothly but hopefully you can get started with that

Basic classes - Nested classes

I have something like this:
public class Ship
{
public void PositionX_pixels_set1(float _position_x){position_x = _position_x;}
public class Engine : Ship
{
public int engines() { return 5; }
public class Piston
{
public int pistons(){return 5;}
}
}
}
void Main
{
Ship ship = new Ship()
int a = ship.Engine.Piston.pistons;//why this not working?
}
I don't know what I'm doing wrong. Why isn't "ship.Engine.Piston.pistons" working?
Because Engine is type inside the Ship class, and its member.
To fix this can do something like:
public class Ship
{
public void PositionX_pixels_set1(float _position_x){position_x = _position_x;}
private void Engine _myEngine = new Engine(); //DEFINE ENGINE MEMBER
public Engine MyEngine { //DEFINE A PROPERTY TO ACCESS THAT MEMBER
get {
return _myEngine;
}
}
public class Engine : Ship
{
public int engines() { return 5; }
private Piston _myPiston = new Piston();//DEFINE PISTON MEMBER
public Piston MyPiston {//DEFINE A PROPERTY TO ACCESS THAT
get {
return _myPiston ;
}
}
public class Piston
{
public int pistons(){return 5;}
}
MEMBER
}
}
and after use it like:
int a = ship.MyEngine.MyPiston.pistons
You must initiate an object of your sub type (Nested type) in order to access its method, in your case you are not creating any object of your nested types so you can not access its methode.
While you create a new Ship() you are not creating any Engine or Piston, so you can not access the methode Pistons() of the non existing object.

Categories

Resources