I am fairly new to arrays in C# and am used to storing a mass of data in a string and in INI files and then breaking it down into basic arrays using delimiters...so yeh, my knowledge is almost none existent.
My main form class begin this definition:
public CAirportData[] _AirportData; //size not known
This is the method I am using to create the array:
...string[] airports = possibleAirports.Split(','); //size is known
foreach (string airport in airports)
{
string[] rwys = inif.Read(airport, "rwys").Split(':'); //size is known (2)
_AirportData = new CAirportData[] { new CAirportData() { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] } };
}
I know this just boils down to my limited knowledge of objects and arrays. But I can't seem to find anything on the internet that uses this sort of thing. I have tried to combine other peoples code with little success.
I need the _AirportData array to be available outside of the form hence public and declared outside of any methods. I supose the main problem is that I am overwriting array and foreach airport I am creating a new array hence loosing the previous. I had tried moving the ..= new CAirportData[] to all sorts of places but Visual Studio doesn't like it.
Below is the class definition for CAirportData:
public class CAirportData
{
public string icao { get; set; }
public string depRwy { get; set; }
public string arrRwy { get; set; }
public override string ToString()
{
string result = string.Format("ICAO: {0}, Dep: {1}, Arr: {2}", this.icao, this.depRwy, this.arrRwy);
return result;
}
}
public class CMRunways
{
public string icao { get; set; }
public string depRwy { get; set; }
public string arrRwy { get; set; }
}
Many thanks in advance for any help!
What you're looking for is generic List. Change the definition to:
public List<CAirportData> _AirportData = new List<CAirportData>();
Then the code in the loop to:
_AirportData.Add(new CAirportData { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] });
This is what I would do...Create a static class, with a static property (airports) and add a static constructor to load the airports from file at the begining.
public static class Session
{
public static CAirportData[] _AirportData;
static Session()
{
string airports = possibleAirports.Split(",");
foreach (string airport in airports)
{
string[] rwys = inif.Read(airport, "rwys").Split(':'); //size is known (2)
_AirportData = new CAirportData[] { new CAirportData() { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] } };
}
}
}
Now you can access the array anywhere in the project like
MessageBox.Show(Session.CAirportData[0].depRwy);
Related
I'm trying to learn how the lists work and I'm quite lost on a concept of using set; get; properties and working with a List. I have a project that I want to populate a list with 2 items that are related to each other (hence a object) Relation and an ID so I created a RelationLink class, the idea is to populate list with these 2 prosperities but using (get; set;) and I'm not quite sure how to do this so it would let me add the properties to a list and retrive a list from a PropertiesRepository as well.
public class PropertiesRepository
{
public class RelationLink
{
public string Relation { get; set; }
public string LinkID { get; set; }
}
public class ListofRelations
{
public List<RelationLink> relList { get; set; }
public void addRelation(RelationLink rel)
{
relList.Add(rel);
}
}
}
the code below fails at listofRelations.addRelation(relationLink) when debugging I can see that its going to add addRelation method and I see the values being passed. However when adding the object to a list nothing is being added. so when get runs it fails due to null exception.
am I doing the setters getters correctly? the part where I'm lost is how can I add the 2 (string) properties to that list in a main program file with setters, that could be read from relList
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
PropertiesRepository repProperties = new PropertiesRepository();
PropertiesRepository.RelationLink relationLink = new PropertiesRepository.RelationLink();
PropertiesRepository.ListofRelations listofRelations = new PropertiesRepository.ListofRelations();
relationLink.Relation = "Relation A";
relationLink.LinkID = "12345";
listofRelations.addRelation(relationLink);
foreach (var elm in listofRelations.relList)
{
Console.WriteLine("Relation from List is " + elm.Relation + "Link ID from List is " + elm.LinkID);
}
}
}
relList in your instance of listofRelations is never initialised with an instance of the list.
public class ListofRelations
{
public List<RelationLink> relList { get; set; } = new List<RelationLink>();
public void addRelation(RelationLink rel)
{
relList.Add(rel);
}
}
you could initialise it like this or in a constructor. Or before you call addRelation you could write if you want.
listOfRelations.relList = new List<RelationLink>();
I have a stucture of classes and subclasses as follows:
public class Regions
{
public const string UNITEDSTATES = "United States";
public static string[] members = { UNITEDSTATES};
public static class UnitedStatesTypes
{
public const string STEEL = "steel";
public const string CONCRETE = "concrete";
public static string[] members = { STEEL, CONCRETE };
public static class SteelStandards
{
public const string A36 = "ASTM A36";
public static string[] members = { A36 };
public static class A36Grades
{
public const string GRADE_36 = "Grade 36";
public static string[] members = { GRADE_36 };
}
public static class ConcreteStandards
{
...
}
There are more values under each one of the classes, but this is just a small sample so you can get the idea of what it looks like. I am trying to create a UI to select each one of these. There are 4 dropdown menus, each menu is populated by the value of the higher menu. So if the standards dropdown is on SteelStandards, the next dropdown is populated with A36, if it was on ConcreteStandards the next would be populated with the data under ConcreteStandards. Is there a way that I can access a subclass using a string variable?
For example, the first dropdown will select United States. The next dropdown needs to piece together "UnitedStatesTypes" and then access Regions.UnitedStatesTypes.members. I have tried using braces
Regions["UnitedStatesTypes"].members
but this did not work. Is there a way to make this happen? Or is there a better way to organize my data?
You could do this with just dictionaries, albeit it gets a bit unwieldy as you go down the tree:
var regions = new Dictionary<string,Dictionary<string,Dictionary<string,Dictionary<string,List<string>>>>>();
// populate it. Yes I know how ugly that will look!
var usSteelStandards = regions["United States"]["Steel"]["Standards"];
A better way might be to refactor your code as a set of class instances, instead of trying to use static classes/members all the way. It is a typical tree structure
public class Node : IEnumerable<Node>
{
public Node(string text)
{
this.Text = text;
this.Children = new List<Node>();
}
public string Text {get; private set;}
public List<Node> Children { get; private set;}
public Node this[string childText]
{
get{ return this.Children.FirstOrDefault(x => x.Text == childText); }
}
public void Add(string text, params Node[] childNodes)
{
var node = new Node(text);
node.Children.AddRange(childNodes);
this.Children.Add(node);
}
public IEnumerator<Node> GetEnumerator()
{
return Children.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
This can then be setup and used much easier
var node = new Node("root")
{
{
"United States",
new Node("Steel")
{
{
"ASTM A36",
new Node("Grade 36")
}
},
new Node("Concrete")
{
}
}
};
Console.WriteLine(node["United States"].Children.Count);
Console.WriteLine(node["United States"]["Steel"]["ASTM A36"].Children[0].Text);
Live example: https://rextester.com/QVGN99585
So I'm making a game, and it saves users' progress on the computer in a binary file. The User class stores a few things:
Integers for stat values (Serializable)
Strings for the Username and the skin assets
Lists of both the Achievement class and the InventoryItem class, which I have created myself.
Here are the User fields:
public string Username = "";
// ID is used for local identification, as usernames can be changed.
public int ID;
public int Coins = 0;
public List<Achievement> AchievementsCompleted = new List<Achievement>();
public List<InventoryItem> Inventory = new List<InventoryItem>();
public List<string> Skins = new List<string>();
public string CurrentSkinAsset { get; set; }
The Achievement class stores ints, bools, and strings, which are all serializable. The InventoryItem class stores its name (a string) and an InventoryAction, which is a delegate that is called when the item is used.
These are the Achievement class's fields:
public int ID = 0;
public string Name = "";
public bool Earned = false;
public string Description = "";
public string Image;
public AchievmentDifficulty Difficulty;
public int CoinsOnCompletion = 0;
public AchievementMethod OnCompletion;
public AchievementCriteria CompletionCriteria;
public bool Completed = false;
And here are the fields for the InventoryItem class:
InventoryAction actionWhenUsed;
public string Name;
public string AssetName;
The source of the InventoryAction variables are in my XNAGame class. What I mean by this is that the XNAGame class has a method called "UseSword()" or whatever, which it passes into the InventoryItem class. Previously, the methods were stored in the Game1 class, but the Game class, which Game1 inherits from, is not serializable, and there's no way for me to control that. This is why I have an XNAGame class.
I get an error when trying to serialize: "The 'SpriteFont' class is not marked as serializable", or something like that. Well, there is a SpriteFont object in my XNAGame class, and some quick tests showed that this is the source of the issue. Well, I have no control over whether or not the SpriteFont class is Serializable.
Why is the game doing this? Why must all the fields in the XNAGame class be serializable, when all I need is a few methods?
Keep in mind when answering that I'm 13, and may not understand all the terms you're using. If you need any code samples, I'll be glad to provide them for you. Thanks in advance!
EDIT: One solution I have thought of is to store the InventoryAction delegates in a Dictionary, except that this will be a pain and isn't very good programming practice. If this is the only way, I'll accept it, though (Honestly at this point I think this is the best solution).
EDIT 2: Here's the code for the User.Serialize method (I know what I'm doing in inefficient, and I should use a database, blah, blah, blah. I'm fine with what I'm doing now, so bear with me.):
FileStream fileStream = null;
List<User> users;
BinaryFormatter binaryFormatter = new BinaryFormatter();
try
{
if (File.Exists(FILE_PATH) && !IsFileLocked(FILE_PATH))
{
fileStream = File.Open(FILE_PATH, FileMode.Open);
users = (List<User>)binaryFormatter.Deserialize(fileStream);
}
else
{
fileStream = File.Create(FILE_PATH);
users = new List<User>();
}
for (int i = 0; i < users.Count; i++)
{
if (users[i].ID == this.ID)
{
users.Remove(users[i]);
}
}
foreach (Achievement a in AchievementsCompleted)
{
if (a.CompletionCriteria != null)
{
a.CompletionCriteria = null;
}
if (a.OnCompletion != null)
{
a.OnCompletion = null;
}
}
users.Add(this);
fileStream.Position = 0;
binaryFormatter.Serialize(fileStream, users);
You cannot serialize a SpriteFont by design, actually this is possible (.XNB file) but it hasn't been made public.
Solution:
Strip it off your serialized class.
Alternatives:
If for some reasons you must serialize some font, the first thing that comes to my mind would be to roll-out your own font system such as BMFont but that's a daunting task since you'll have to use it everywhere else where you might already do ...
Generate a pre-defined amount of fonts (i.e. Arial/Times/Courier at size 10/11/12 etc ...) using XNA Content app (can't recall its exact name); then store this user preference as two strings. With a string.Format(...) you should be able to load the right font back quite easily.
Alternative 2 is certainly the easiest and won't take more than a few minutes to roll-out.
EDIT
Basically, instead of saving a delegate I do the following:
inventory items have their own type
each type name is de/serialized accordingly
their logic does not happen in the main game class anymore
you don't have to manually match item type / action method
So while you'll end up with more classes, you have concerns separated and you can keep your main loop clean and relatively generic.
Code:
public static class Demo
{
public static void DemoCode()
{
// create new profile
var profile = new UserProfile
{
Name = "Bill",
Gold = 1000000,
Achievements = new List<Achievement>(new[]
{
Achievement.Warrior
}),
Inventory = new Inventory(new[]
{
new FireSpell()
})
};
// save it
using (var stream = File.Create("profile.bin"))
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, profile);
}
// load it
using (var stream = File.OpenRead("profile.bin"))
{
var formatter = new BinaryFormatter();
var deserialize = formatter.Deserialize(stream);
var userProfile = (UserProfile) deserialize;
// set everything on fire :)
var fireSpell = userProfile.Inventory.Items.OfType<FireSpell>().FirstOrDefault();
if (fireSpell != null) fireSpell.Execute("whatever");
}
}
}
[Serializable]
public sealed class UserProfile
{
public string Name { get; set; }
public int Gold { get; set; }
public List<Achievement> Achievements { get; set; }
public Inventory Inventory { get; set; }
}
public enum Achievement
{
Warrior
}
[Serializable]
public sealed class Inventory : ISerializable
{
public Inventory() // for serialization
{
}
public Inventory(SerializationInfo info, StreamingContext context) // for serialization
{
var value = (string) info.GetValue("Items", typeof(string));
var strings = value.Split(';');
var items = strings.Select(s =>
{
var type = Type.GetType(s);
if (type == null) throw new ArgumentNullException(nameof(type));
var instance = Activator.CreateInstance(type);
var item = instance as InventoryItem;
return item;
}).ToArray();
Items = new List<InventoryItem>(items);
}
public Inventory(IEnumerable<InventoryItem> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
Items = new List<InventoryItem>(items);
}
public List<InventoryItem> Items { get; }
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
var strings = Items.Select(s => s.GetType().AssemblyQualifiedName).ToArray();
var value = string.Join(";", strings);
info.AddValue("Items", value);
}
#endregion
}
public abstract class InventoryItem
{
public abstract void Execute(params object[] objects);
}
public abstract class Spell : InventoryItem
{
}
public sealed class FireSpell : Spell
{
public override void Execute(params object[] objects)
{
// using 'params object[]' a simple and generic way to pass things if any, i.e.
// var world = objects[0];
// var strength = objects[1];
// now do something with these !
}
}
Okay, so I figured it out.
The best solution was to use a Dictionary in the XNAGame class, which stores two things: an ItemType (an enumeration), and an InventoryAction. Basically, when I use an item, I check it's type and then look up it's method. Thanks to everyone who tried, and I'm sorry if the question was confusing.
I have a problem where I need to store a List in another Class/List.
I have this class:
public class InformationMyTravels
{
public string MyTravelsDate { get; set; }
public string MyTravelsFromLocation { get; set; }
public string MyTravelsToLocation { get; set; }
}
And I can populate a List and then save it to my isolated storage (AppSettings).
The problem is that when I have more than one "Overview" below, then the following code will just append the travel history.
What I need is a separation of "Overview", so that the List I populate for each Overview is saved in another Class/List, which can contain the x-number of "Overview" lists I fetch.
private async Task Fetch()
{
AppSettings localStorage = new AppSettings();
List<InformationMyTravels> mytravelsreturned = new List<InformationMyTravels>();
// I need to separate the returned data per Overview
foreach (Overview loaded in localStorage.OverviewSetting)
{
string mytravelsHtml = await WebRequests.LoadPageAsyncSpecificRKMyTravels(loaded.CardOverviewID);
HtmlDocument htmlDocumentmytravels = new HtmlDocument();
htmlDocumentmytravels.LoadHtml(mytravelsHtml);
foreach (HtmlNode table in htmlDocumentmytravels.DocumentNode.SelectNodes("//table[#class='table']"))
{
foreach (HtmlNode row in table.SelectNodes("tr"))
{
InformationMyTravels newTravel = new InformationMyTravels();
newTravel.MyTravelsDate = row.SelectSingleNode("td[1]").InnerText.Trim();
newTravel.MyTravelsFromLocation = row.SelectSingleNode("td[3]").InnerText.Trim();
newTravel.MyTravelsToLocation = row.SelectSingleNode("td[5]").InnerText.Trim();
// Here it just appends with newTravel's
mytravelsreturned.Add(newTravel);
}
}
mytravelsreturned.Reverse();
}
localStorage.MyTravelsSetting = mytravelsreturned;
}
So how do I take "mytravelsreturned" and add this to another Class/List?
And afterwards I need to select the specific listindex from the new class and load the travels into a listbox.ItemsSource
Wanted hierarchy:
Class/List
InformationMyTravels (0)
MyTravelsDate(0)
MyTravelsFromLocation (0)
MyTravelsToLocation(0)
MyTravelsDate(1)
MyTravelsFromLocation (1)
MyTravelsToLocation(1)
etc.
InformationMyTravels (1)
MyTravelsDate(0)
MyTravelsFromLocation (0)
MyTravelsToLocation(0)
MyTravelsDate(1)
MyTravelsFromLocation (1)
MyTravelsToLocation(1)
etc.
I then need to load e.g InformationMyTravels (1) into a listbox.ItemsSource
I hope it makes sense.
Below is the untested code(I am not on Windows currently). It should give you the Idea.
Class InformationMyTravelsList<T> : List<T>
{
public int id{get; private set;}
public void AddInfo(int ID, InformationMyTravels info)
{
this.id = ID;
this.Add(info);
}
}
Class Main
{
void myLogic()
{
InformationMyTravels info = new InformationMyTravels();
InformationMyTravelsList<InformationMyTravels> infoList = new InformationMyTravelsList<InformationMyTravels>();
infoList.AddInfo(info); //add as many you want
List<InformationMyTravelsList<InformationMyTravels>> myList = new InformationMyTravelsList<InformationMyTravels>>();
myList.Add(infoList);
myListBox.DataSource = myList;
}
}
I've written a DLL that parses XML and returns Dictionary with tag name and its value. I'm using it in other program called ZennoPoster Project Maker. Here's the code:
XMLWork.XMLWorker worker = new XMLWork.XMLWorker(); // My parse class
string path = #"Z:\New\test.xml";
Dictionary<string, string> data = worker.GetData(path); // GetData - method, that returns
// data from XML
project.Variables["second_name"].Value = data["second_name"];
This block of code I must remake into XMLWorker class method and return a project type and in ZennoPoster I have to with 1 line of code return data. How can I do it?
Assuming that you have the following simplified types:
namespace Objects
{
public class Project
{
public Dictionary<string, Variable> Variables { get; set; }
}
public class Variable
{
public object Value { get; set; }
}
}
You could structure your XMLWorker class like this:
using Objects;
public class XMLWorker
{
public Project Project { get; private set; }
public XMLWorker(string path)
{
Project = new Project();
Dictionary<string, string> data = GetData(path);
Project.Variables["second_name"].Value = data["second_name"];
}
internal Dictionary<string, string> GetData(string path)
{
// method implementation
}
}
Sample usage:
var project = (new XMLWork.XMLWorker(#"Z:\New\test.xml")).Project;