I'm trying to add new values input by the user on a separate windows form into the following array:
public class NameValue
{
public string Name;
public string Value;
public NameValue() { Name = null; Value = null; }
public NameValue(string name, string value) { Name = name; Value = value; }
}
public class DefaultSettings
{
public static NameValue[] Sites = new NameValue[]
{
new NameValue("los angeles, CA", "http://losangeles.craigslist.org/"),
};
public static NameValue[] Categories = new NameValue[]
{
new NameValue("all for sale", "sss"),
};
}
How do I add the new values to the array while keeping the values of the old array?
Edit
I tried using Mr. Noren's function:
static void AddValueToSites(NameValue newValue)
{
int size = DefaultSettings.Sites.Length;
NameValue[] newSites = new NameValue[size + 1];
Array.Copy(DefaultSettings.Sites, newSites, size);
newSites[size] = newValue;
DefaultSettings.Sites = newSites;
}
private void button1_Click(object sender, EventArgs e)
{
NameValue newSite = new NameValue("Test, OR", "http://portland.craigslist.org/");
AddValueToSites(newSite);
Close();
}
But that's not working... The class I am getting data from is:
public partial class Location : Office2007Form
{
public Location()
{
InitializeComponent();
}
static void AddValueToSites(NameValue newValue)...
private void button1_Click(object sender, EventArgs e)...
}
You cannot change the size of an array, ever. You would need to use something like a List for that.
Since you're using name/value pairs you should consider using Dictionary<TKey,TValue>.
Finally, if you're looking to have different classes contribute to the contents of the arrays, then that's not going to happen.
You can't add to an array, whether it's in another class or not.
Use List<NameValue> instead of NameValue[] and then you can use Sites.Add(...).
If you're absolutely married to the idea of using an array versus some more flexible collection, you should implement some AddX() methods in the class that is defining the base values. Those methods would take care of inserting the new values in the array. If you're not concerned with multithreading issues, it can be very simple:
(Warning: code is from my head and not tested)
static void AddValueToSites(NameValue newValue)
{
int size = Sites.Length;
NameValue[] newSites = new NameValue[size+1];
Array.Copy(Sites, newSites, size);
newSites[size] = newValue;
Sites = newSites;
}
And again for Categories.
Like others suggested, using List<NameValue> or Dictionary<string,string> would be options that would be more fluid. These already have Add, Remove, Contains, etc. - basically all you need when you need to manipulate arrays.
Arrays are not mutable in place, so if you resize them (using any approach) it'll result in a different instance being created (this is what happens in VB.NET's ReDim but they hide it from you).
The simplest way to resize an array in C# is to use the Concat extension method (requires System.Linq):
string[] myArray = new string[] { "Hello", "World" };
myArray = myArray.Concat(new string[] { "Something", "new" };
myArray will now be 4 elements deep. I don't think this will work between classes though (haven't tried though).
Related
I have a project called FoodPantryGlobal, which contains all of the constants I will use.
I have another project called RegisterInput which uses FoodPantryGlobal.
In FoodPantryGlobal is an array called CityNames[], that RegisterInput needs to use in order to load a Combobox.
The problem I am having is CityNames[] seems to be empty to RegisterInput when called.
If I declare a local array, and use that every thing works fine.
I am betting it is something simple that I am missing.
FoodPantryGlobal has been added to the resources.
using System;
The code I am using for the Global.
namespace FoodPantryGlobals
{
public class GlobalConst
{
public const string excelFileName = "Food Pantry Registry.xlsm";
public const int cityNamesMembers = 4;
public readonly string[] cityNames = new string[cityNamesMembers] {"Hollans", "Roanoke", "Salem", "Vinton" };
}
public class GlobalProcedures
{
}
}
The code making trying to use CityNames.
using FoodPantryGlobals;
private void CboxCity_GotFocus(Object sender, EventArgs e)
{
const int members = GlobalConst.cityNamesMembers;
//string[] cityNames = new string[members] { "Hollans", "Roanoke", "Salem", "Vinton" };
//string city = FoodPantryGlobals.GlobalConst.cityNames[0];
int index = 0;
while (index < members)
{
string city = GlobalConst.cityNames[index];
CboxCity.Items.Add(city);
index = index + 1;
}
}
Just make cityNames static
public static readonly string[] cityNames = new string[] { "Hollans", "Roanoke", "Salem", "Vinton" };
and IMHO you can use foreach
foreach (var city in GlobalConst.cityNames) CboxCity.Items.Add(city);
or maybe even better
CboxCity.Items.AddRange( GlobalConst.cityNames);
maybe you should clear previous items before adding new ones
CboxCity.Items.Clear();
I have created a dictionary named Teams and I use a struct to make hold an integer, a string and a boolean.
So if anyone joins a red team, the bool will be false to prevent other players from joining same team.
I tried to set the boolean to false but it failed.
public Dictionary <int , myCustomType> Teams = new Dictionary<int,myCustomType>();
private ExitGames.Client.Photon.Hashtable m_PlayerCustomPropeties = new ExitGames.Client.Photon.Hashtable();
private void addTeams()
{
myCustomType t=new myCustomType();
t.name="Yellow";
t.flag= true;
Teams.Add(1,t);
t.name="Red";
t.flag= true;
Teams.Add(2,t);
t.name="Blue";
t.flag= true;
Teams.Add(3,t);
t.name="Green";
t.flag= true;
Teams.Add(4,t);
Debug.Log("Teams created.");
}
public void getPlayers()
{
Debug.Log(Teams[1].flag);
Teams[1] = new myCustomType { flag = false };
}
Your type is defined as:
public struct myCustomType
{
public string name;
public bool flag;
}
And Teams is a Dictionary<int, myCustomType>. When you tried something like:
Teams[1].flag = false;
it failed because Teams[1] is just a value (the return value of the Dictionary<,> indexer getter).
Your type myCustomType is a mutable struct. The struct is returned by value, so it makes no sense to try to modify the returned copy of the value.
You will need:
var mct = Teams[1]; // 'mct' is a copy of the value from the 'Dictionary<,>'.
mct.flag = false; // You modify the copy which is allowed because 'mct' is a variable.
Teams[1] = mct; // You overwrite the old value with 'mct'.
Some people consider mutable structs evil.
This seems like an anti-pattern. Make your struct immutable (you may not want a struct for this anyway, especially if the team needs other functionality):
public struct myCustomType
{
public string Name { get; }
public myCustomType(string name)
{
this.Name = name;
}
}
Create a set of available teams, which you populate similarly to your addteams method:
public Dictionary<string, myCustomType> AvailableTeams; //color, team
public void InitializeTeams()
{
AvailableTeams = new Dictionary<string, myCustomType>()
{
["Yellow"] = new myCustomType("Yellow"),
["Red"] = new myCustomType("Red"),
["Blue"] = new myCustomType("Blue"),
["Green"] = new myCustomType("Green")
};
}
When a player joins a team, remove that team from the available set, and add it to a set of ActiveTeams:
public Dictionary<int, myCustomType> ActiveTeams; //player #, team
public void JoinTeam(int playerNumber, string teamColor)
{
if (!AvailableTeams.TryGetValue(teamColor, out myCustomType team)
// handle team already taken.
ActiveTeams.Add(playerNumber, team);
AvailableTeams.Remove(teamColor);
}
I have been given some code that has objects composed of lists of different types. A simple example of what I mean:
public class Account
{
private long accountID;
private List<string> accountHolders;
private List<string> phoneNumbers;
private List<string> addresses;
public Account()
{
this.accountHolders = new List<string>();
this.phoneNumbers = new List<string>();
this.addresses = new List<string>();
}
public long AccountID
{
get
{
return this.accountID;
}
set
{
this.accountID = value;
}
}
}
For a requirement I need to get the total amount of elements in each list for validation purposes. I have the following method which works:
public class AccountParser
{
// Some code
public int CountElements(Account acct)
{
int count = 0;
count += acct.accountHolders.Count();
count += acct.phoneNumbers.Count();
count += acct.addresses.Count();
return count;
}
}
but was wondering if there was a better way to do this. I know I can enumerate over a List with Linq but I can't seem to get it to work in this case.
What you're doing is the right thing
You could do it in one line without declaring any variable
public int CountElements(Account acct)
{
return acct.accountHolders.Count() + acct.phoneNumbers.Count() + acct.addresses.Count();
}
But it doesn't change much.
The ammount of lists is static, because the class is static, so it doesn't make sense to use Reflection if the structure wont change.
Now you could have more than one Account classes with different types of lists. In that case, i would create an abstract AbsAccount class, that has an abstract CountElements property:
public abstract class AbsAccount
{
public abstract int CountElements { get; }
}
public class Account: AbsAccount
{
private List<string> accountHolders;
private List<string> phoneNumbers;
private List<string> addresses;
public override int CountElements
{
get
{
return this.accountHolders.Count()
+ this.phoneNumbers.Count()
+ this.addresses.Count();
}
}
}
public class AccountParser
{
// Some code
public int CountElements(AbsAccount acct)
{
return acct.CountElements;
}
}
But maybe im taking it too far...
You can add items to a list then call .Summethod on it, but it's not better from performance point of view.
public class AccountParser
{
// Some code
public int CountElements(Account acct)
{
List<string> all = new List<string>();
all.AddRange(acct.accountHolders);
all.AddRange(acct.phoneNumbers);
all.AddRange(acct.addresses);
return all.Count();
}
}
Another approach will be (because I can see you are not exposing directly your lists) to use observer pattern, and update the number of elements in another field or even list, every time you are updating one of your lists. Then get the value from that field, but I think the best way is the one you have already adopted.
I'd like to parse a text file with a few dozen entries. Right now, I have a dumbed-down solution that reads line by line and compares against hard-coded strings:
while ((line = reader.ReadLine()) != null) //returns null if end of stream
{
cmpStr = "MODE";
try
{
if (line.Equals(cmpStr))
GlobalData.mode = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
catch { }
cmpStr = "TIME_YEAR";
try
{
if (line.Equals(cmpStr))
GlobalData.time_year = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
catch { }
// ... repeat to parse the remaining lines
}
GlobalData is a static class and looks like this:
public static class GlobalData
{
public static int mode;
public static int time_year;
public static int time_month;
public static int time_day;
public static int time_hour;
public static int time_minute;
// other entries omitted
public static string[] GlobalKeywords = new string[37]
{
"MODE",
"TIME_YEAR",
"TIME_MONTH",
"TIME_DAY",
"TIME_HOUR",
"TIME_MINUTE",
// other entries omitted
};
}
If it were possible to access my static fields by index, I'd do:
int i = 0;
while ((line = reader.ReadLine()) != null)
{
cmpStr = GlobalData.GlobalKeywords[i]; // when i == 0: cmpStr = "MODE"
if (line.Equals(cmpStr))
GlobalData[i] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
// GlobalData[0] would be GlobalData.mode, and so on (but doesn't work)
i++;
}
catch { }
So, even though I can setup a loop to compare against a string array of keywords,
how do I assign a certain field of my static class ?
br
Chris
I'm not sure what your business constraints are, so it's hard to propose a fool-proof solution, though a few points:
cmpStr = "MODE";
try
{
if (line.Equals(cmpStr))
GlobalData.mode = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
This won't work as you (probably expect) - if line.Equals("MODE") then line.Remove(0, "MODE".Length) is an empty string. What you probably want is line.StartsWith(cmpStr) or line.Contains(cmpStr).
GlobalData is a static class
This doesn't seem a good approach for what you're doing. You may want to read up on static classes and when to use them (MSDN is a good starting point, though it obviously can't cover everything: http://msdn.microsoft.com/en-us/library/79b3xss3%28v=vs.80%29.aspx).
Other than that, you can probably simply replace all your int fields with a dictionary (though please rethink the static approach as described above):
public static Dictionary<String, int> Items = new Dictionary<String, int>();
Then your parsing code could look like this:
while ((line = reader.ReadLine()) != null) //returns null if end of stream
{
var matchingString
= GlobalData.GlobalKeywords.FirstOrDefault(s => line.StartsWith(s));
if (matchingString != null)
GlobalData[matchingString]
= Convert.ToInt32(line.Remove(0, matchingString.Length));
}
You will then be able to fetch that data using e.g. GlobalData.Items["MODE"].
One last bit: you may consider introducing constant values in your global data class, e.g.:
public const String MODE = "MODE";
Then you can use GlobalData.Items[GlobalData.MODE] and avoid typos: writing GlobalData.Items[GlobalData.MODe] would cause a compile error.
Replace this:
public static int mode;
public static int time_year;
public static int time_month;
public static int time_day;
public static int time_hour;
public static int time_minute;
With this:
public static Dictionary<string, int> my_values = new Dictionary<string, int>();
Then replace:
GlobalData[i] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
with:
GlobalData.my_values[cmpStr] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
That should do what you want even though I don't understand how you expect the Convert.ToInt32 to work. The way you are calling Remove will create an empty string (which might convert to 0, I can't remember) and even if it didn't, the line doesn't contain a number because you compared it successfully to a string like "MODE".
An elegant way to solve your problem is to prepare a different action for each of the acceptable strings. You use a Dictionary(Of String, <Action>) where Action is a common delegate type that receive a string in input and know how to process it accordingly to the keyword present at the beginning of the line.
// The common signature for every methods stored in the value part of the dictionary
public delegate void ParseLine(string line);
// Global dictionary where you store the strings as keyword
// and the ParseLine as the delegate to execute
Dictionary<String, ParseLine> m_Actions = new Dictionary<String, ParseLine>() ;
void Main()
{
// Initialize the dictionary with the delegate corresponding to the strings keys
m_Actions.Add("MODE", new ParseLine(Task1));
m_Actions.Add("TIME_YEAR", new ParseLine(Task2));
m_Actions.Add("TIME_MONTH", new ParseLine(Task3));
m_Actions.Add("TIME_DAY", new ParseLine(Task4));
.....
while ((line = reader.ReadLine()) != null)
{
// Search the space that divide the keyword from the value on the same line
string command = line.Substring(0, line.IndexOf(' ')).Trim();
// a bit of error checking here is required
if(m_Actions.ContainsKey(command))
m_Actions[command](line);
}
}
void Task1(string line)
{
// this will handle the MODE line
GlobalData.Mode = Convert.ToInt32(line.Substring(line.IndexOf(' ')+1).Trim());
}
void Task2(string line)
{
GlobalData.time_year = Convert.ToInt32(line.Substring(line.IndexOf(' ')+1).Trim());
}
void Task3(string line)
{
.....
}
void Task4(string line)
{
.....
}
A simple (and not really clean) approach is to add an indexer to your global data class and decide which field to set based on the index. But you have to extend the indexer every time you add a field (basically you move the if/switch from the while Loop into the indexer).
You could also use reflection, if you can match the keyword to the field name. This is not very performant but does not need to be extended as long as you can map the keyword to the new field name.
Another approach is to create a dictionary>. In this dictionary you register the keywords, e.g. (pseudo-code):
Class Level variable:
private keywordsDict = new Dictionary<string, Action<int>>();
In a constructor:
keywordsDict.Add("MODE", delegate(value) GlobalData.mode = value);
In while-loop:
var action = keywordsDict[line];
action(value);
In the later approach, you only need to extend the dictionary but not the algorithm as such if you have a new keyword/field.
May be i can tell you how to achieve it (GlobalData[i]) in C# thought its not the answer you are looking for.
class GlobalData
{
private string[] array = new string[10];
public GlobalData()
{
//now initialize array
array[0] = "SomeThingA";
array[1] = "SomeThingB";//continue initialization.
}
public string this[int index]
{
get {return array[index];}
}
}
Now the clients can use GlobalData like ,
GlobalData gd = new GlobalData();
gd[1] = "SomeOtherThing" ; //set the value.
string value = gd[1];//get the value
But this cant be done by making the class static as you see it works with 'this'
related to this topic:
https://stackoverflow.com/questions/15170054/context-hint-using-combobox
Is there a way I can use the strings in my separate class:
namespace KeyWord
{
public class KeyWord
{
//Definitions
public String[] keywords = { "abstract", "as", "etc." };
}
}
to mylistbox items in my mainform?
lb = new ListBox();
Controls.Add(lb);
ty in advance
Sure. Try something like this.
KeyWord kw = new KeyWord();
foreach (string str in kw.keywords)
{
lb.Items.Add(str);
}
Or you can use databinding.
Also, if all you're doing is getting an array of strings from that class, you might want to use a static property so you don't have to instantiate an instance of that object. I would recommend using properties either way for exposing public data, instead of a public field.
Here's an example of using a static property, instead:
public class KeyWord
{
// Private field, only accessible within this class
private static string[] _keywords = { "abstract", "as", "etc." };
// Public Static Property, accessible wherever
public static string[] Keywords
{
get { return _keywords; }
set { _keywords = value; }
}
}
Then:
foreach (string str in KeyWord.Keywords)
{
lb.Items.Add(str);
}
Notice, I didn't instantiate the class in this example (no new KeyWords())