![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
Newbie
Join Date: Jul 2008
Location: Seattle, WA
Posts: 6
Rep Power: 0
![]() |
Values being passed by reference?
So I'm trying to use an object ("InfoStorer") to store a dictionary ("InfoHolder") and some functions related to entering & retrieving the information. Everything seems fine when I store the information, yet when I try to retrieve it nothing happens.
I'm pretty sure it's either my ClearTextBoxes method nulling all the values in my TextBoxes and thus nulling the values in my dictionary too. So my question is is there a better way to clear the text boxes? Will I need to deep clone my List<TextBox>, which from my reading online seems overly difficult? Or am I completely off base and the problem is somewhere else? By the way, I'm just getting a good grasp on C#, so any other recommendations for my code would be welcome. Thanks for any and all help in advance ![]() namespace ClaimaintInfo
{
partial class Form1
{
ClaimaintInfoStorer InfoStorer = new ClaimaintInfoStorer();
public List<TextBox> packer() // Pulls the strings from the "TextBox"es to store - Also Empties "TextBox"es.
{
List<TextBox> info = new List<TextBox>();
info.Add(NameTextBox);
info.Add(CellTextBox);
info.Add(PhoneTextBox);
info.Add(AddressTextBox);
info.Add(POBoxTextBox);
info.Add(CityTextBox);
info.Add(StateTextBox);
info.Add(ZipTextBox);
info.Add(EmailTextBox);
// clearTextBoxes(info);
return info;
}
internal static void clearTextBoxes(List<TextBox> info)
{
if (info[0].Text != null) // Clears "TextBox"es .Text - Checks to see if they need to be cleared.
{
int x = 0; // For iterating through info's "TextBox"es
do
{ // Makes "TextBox"es .Text null.
info[x].Text = null;
x++;
} while (x < info.Count);
}
}
}
public class ClaimaintInfoStorer // Class to handle info saving/loading of info - TEMP until I start working with DBs
{
/* Object to store Claimaint Info in. */
internal Dictionary<string, List<TextBox>> infoHolder = new Dictionary<string, List<TextBox>>();
public void HoldInfo(List<TextBox> tempInfoHolder) // Store info by adding to infoHolder
{
if (infoHolder.ContainsKey(tempInfoHolder[0].Text))
{
}
else
{
infoHolder.Add(tempInfoHolder[0].Text, tempInfoHolder); // tempInfoHolder as a value for my dict needs to be a list of strings
}
}
public List<TextBox> GetInfo(string name) // Method to retrieve info from InfoHolder
{
List<TextBox> tempHolder = new List<TextBox>();
foreach (KeyValuePair<string, List<TextBox>> pair in infoHolder)
{
if (pair.Key == name)
tempHolder = pair.Value;
}
return tempHolder;
}
}
}namespace ClaimaintInfo
{
partial class Form1
{
private void MouseClickSaveButton(object sender, EventArgs e)
{
}
private void MouseClickAddClaimaint(object sender, EventArgs e)
{
InfoStorer.HoldInfo(packer());
ClaimatsListBox.Items.Add(packer()[0].Text);
/* clearTextBoxes(tempDeleter); */
}
private void DoubleClickLoadClaimaint(object sender, EventArgs e)
{
string name = ClaimatsListBox.SelectedItem.ToString();
List<TextBox> tempPacker = packer();
List<TextBox> tempHolder = InfoStorer.GetInfo(name);
for (int i = 0; i < tempHolder.Count; i++) // Unpacks info from my List<TextBox> returned from InfoStorer.GetInfo(name) into "TextBox"es
tempPacker[i].Text = tempHolder[i].Text;
}
}
} |
|
|
|
|
|
#2 |
|
SEXY SHOELESS GOD OF WAR!
![]() Join Date: Jun 2005
Location: Wet west coast of Canada
Posts: 1,197
Rep Power: 5
![]() |
Re: Values being passed by reference?
Note: you might want to wander over to 'Community Introductions' and make an intro thread.
![]() Your problem is that you're handing off references to the textboxes, rather than the strings. Thus, when you clear the textboxes, you're left holding references to textboxes without any data. Another, larger issue is that this closely couples your logic (ie, your ClaimantInfo class- yes, you spelled it wrong :p) with your user interface. Try changing the interface, such as by making it into a console app, or web app, and you'll suddenly have big issues, and you'll need to rewrite the class. Now, while you could instead store <textboxname>.Text in your list (making it a List<string> rather than List<TextBox>), I think your best bet is to rewrite this, and use an approach like so:Have your ClaimantInfo class contain the various fields, such as name, address, phone number, etc. These will all be private data members of the appropriate type (many will be string, but some, like zip code, can be int); just use your judgment to decide on the type.After that, make some public properties that your other classes (such as the main form) can use. Here's an example of the ClaimantInfo class: C# Syntax (Toggle Plain Text)
txtName, txtAddress, and so on. There is also a multiline textbox called txtDisplay (this is used to display the list). Lastly, there are labels for each textbox (so the user knows what the field is for), and two buttons called btnAdd and btnDisplay. The form looks like this:![]() The code (excluding the designer-generated stuff, which is huge and pointless to post, since you don't have to actually write it) looks like so: C# Syntax (Toggle Plain Text)
Anyways, I'll explain a few things in case you're confused by the code. First, in the ClaimantInfo class, note how I have private member variables (I like to prefix them with an underscore, to make it clear, but this is just a naming convention of mine). I also have properties, which are basically methods (in fact, they actually compile down to methods with names like get_Name() and such). However, you access them as though they were public fields (ie, public data members). If you look at various controls, they use properties extensively (the Text property, the ForeColor property, etc). Basically, when you're in the designer, all those things you see listed in the Properties window are- you guessed it- properties.Now, you might be wondering why we prefer properties to actually exposing a field (ie, making the field itself public). First is that you should never make your class state data accessible unless you need to. Make it protected if you intend the class to be inherited, and want subclasses to have access. Otherwise, make it private. If you need class A to have access to some of class B's members, but don't want it accessible to everyone, use internal or protected internal access. This way, other code can't muck about with your class's guts, and possibly assign invalid values and otherwise compromise your class's integrity. Obviously, there are exceptions; if you have constants or read-only fields (fields marked with the readonly keyword can only be initialized in the class's constructor), then exposing these directly is just as safe as using a property (though it might violate your naming conventions).A second, and more important, reason to use properties is that since it's basically a method call, we can do much more complex operations than simply setting or returning a value. For the get property, you can calculate values on the fly and return those. For the set property, you can do validation. Imagine if we used some kind of regular expression validation (look up 'regular expressions' if you don't know what they are) to ensure that the email address field actually was in the correct format (ie username@domain). Another benefit is you can update other fields and call other code. For example, when you change the Text property of txtSomething, it needs to redraw itself with the new text. If Text was a public field instead of a property, you'd need to follow every change with a call such as txtSomething.Update().Anyways, that's why we use properties. As to how, you just have the property type and name, followed by braces, and inside those braces, you have either a get or set accessor, or both (a property that only has one is either read-only or write-only). Both accessors will have another set of braces, as you can see in the ClaimantInfo class I posted. For a get accessor, it mustreturn something matching the property type (ie, the exact type, or for properties whose type is a class or interface, a compatible type such as the declared class type or one derived from it, or a class or struct implementing the declared interface). If this is Greek to you, just remember it needs to return the type you declared it as (a string property's get must return a string, and so on). For a set accessor, it doesn't need to do anything- the code will compile fine- but obviously, if you didn't want to write the code, you'd just make it a read-only property. If you do write code in the set accessor, the name value is a placeholder for what was passed in to the property. Remember I said properties are basically methods? Well, a set accessor will basically compile to the method void set_xxx(<datatype> value). Thus, if I say txtSomething.Text = "hello world", then inside the property's set accessor, value will be of type string (this is because that's the type of the property) and hold "hello world" (because that's what was passed in).Anyways, that's it for the ClaimantInfo class. As far as the form code goes, it's pretty straightforward. I'm not sure if you've used string.Format() before, but if not, check it out on MSDN. It's a very handy method. Another method I've used is the int.TryParse() method. This takes two parameters (at least, the version I used here does). The first is a string to try to parse, and the second is a reference to an int. Note the use of the out keyword rather than ref. Both are the same, except out tells the compiler that it's okay if the variable hasn't been initialized, so it doesn't complain about 'use of unassigned local variable', and puke on your shoes. The method returns a bool, with true indicating it was successfully parsed. If you're interested, the other basic types (long, float, etc) have TryParse() methods of their own that work in a similar manner. I much prefer using this method to Parse(), as the latter will throw an exception if the data can't be parsed. I'd much rather have a true/false return value that simply tells me whether or not it worked, and if not, I can pop up a message bitching at the user.Anyways, like many of my posts, this has been pretty long-winded (your word of the day: loquacious). Let me know if you have any questions about the code.
__________________
And once again, Probability proves itself willing to sneak into a back alley and service Drama as would a copper-piece harlot. - Vaarsuvius, Order of the Stick |
|
|
|
|
|
#3 |
|
Professional Programmer
|
Re: Values being passed by reference?
WOW lectricpharaoh - you have way too much free time
![]()
__________________
Don't take life too seriously, it's not permanent ! |
|
|
|
|
|
#4 |
|
Unknown
Join Date: Apr 2008
Location: unknown
Posts: 87
Rep Power: 1
![]() |
Re: Values being passed by reference?
lectricpharaoh is one of my favorite... if there's goin to be the most dilligent PFO member its gotta be lectricpharaoh
__________________
------------------------------------------------------------------------- I thought what I'd Do was, I'd pretend to be one of those deaf mutes ------------------------------------------------------------------------ |
|
|
|
|
|
#5 |
|
Newbie
Join Date: Jul 2008
Location: Seattle, WA
Posts: 6
Rep Power: 0
![]() |
Re: Values being passed by reference?
Wow, that was way more helpful than I could've expected.
I understand about the public/private/internal stuff and the get/set stuff, but your explanations gave me a few more insights into how they work. I guess I shouldn't cut corners when I'm learning and make most things public eh? lol I'll play with your code some and post again if I have any specific questions. Thanks again. |
|
|
|
|
|
#6 | |||
|
SEXY SHOELESS GOD OF WAR!
![]() Join Date: Jun 2005
Location: Wet west coast of Canada
Posts: 1,197
Rep Power: 5
![]() |
Re: Values being passed by reference?
Quote:
![]() Quote:
![]() Quote:
__________________
And once again, Probability proves itself willing to sneak into a back alley and service Drama as would a copper-piece harlot. - Vaarsuvius, Order of the Stick |
|||
|
|
|
|
|
#7 |
|
Newbie
Join Date: Jul 2008
Location: Seattle, WA
Posts: 6
Rep Power: 0
![]() |
Re: Values being passed by reference?
Well I played around with it, have a few more questions/issues now though. First off, I don't seem to be using the get/set properties; are they just there for future expansion? If I continue to not use them should I just delete them?
Secondly, I don't think I fully understand how to seperate my logic from my UI code. I can't access my TextBoxes outside of the partial ClaimantUI class, should I change my TextBox objects to "internal" instead of "private" so I can work with them outside of my events? Or did I misunderstand and it's best keep that with the UI, so if I ever needed to run this on a console I wouldn't have extra code floating around that does nothing because I'd never use text boxes in a console? Also, I tried having the zip as an int (in the long run I plan to do that) but it gets angry at me if I don't enter a zip code and tries to parse and empty string to an int. So that brings me to my other issue; What if I don't have all the information but want to save the first/last name, phone, and email? Do I need another method overloader for each combination of information, or is there an easier more elegant way of doing that? Maybe I'm just making it too complex. The basic purpose for this code is store contact info for various insurance claimants; the in house program we use at work currently has no way of storing that information except in a section used for making notes on phone calls and it is a royal pain to hunt through my notes to find someone's address. Some claims can have multiple claimants and when I am first notified of a claim I don't always have all their contact info. Sometimes it's just a name and address, other times it's a name and phone number, sometimes it's just a name. Also my boss should be setting me up with a database soon that I can play around with, so I can just use that store my info instead of a dictionary<> soon. So here's a screet shot of my UI, just a note I do plan on adding a a larger textbox for easy copying/pasting of the info soon - ![]() And my code - class ClaimantInfo
{
private string _firstName;
private string _lastName;
private string _address;
private string _poBox;
private string _city;
private string _state;
private string _zip; // spits out errors as an int when trying to convert an empty string to an int.
private string _phone;
private string _cell;
private string _email;
/* get/set properties for above listed private variables */
public string firstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string lastName
{
get { return _lastName; }
set { _lastName = value; }
}
public string address
{
get { return _address; }
set { _address = value; }
}
public string poBox
{
get { return _poBox; }
set { _poBox = value; }
}
public string city
{
get { return _city; }
set { _city = value; }
}
public string state
{
get { return _state; }
set { _state = value; }
}
public string zip
{
get { return _zip; }
set { _zip = value; }
}
public string phone
{
get { return _phone; }
set { _phone = value; }
}
public string cell
{
get { return _cell; }
set { _cell = value; }
}
public string email
{
get { return _email; }
set { _email = value; }
}
/* Initialize ClaimantInfo and assign base variables */
public ClaimantInfo(string firstName, string lastName, string address, string poBox,
string city, string state, string zip, string phone, string cell, string email)
{
_firstName = firstName;
_lastName = lastName;
_address = address;
_poBox = poBox;
_city = city;
_state = state;
_zip = zip;
_phone = phone;
_cell = cell;
_email = email;
}
public ClaimantInfo(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
}public partial class ClaimantUI : Form
{
private Dictionary<string, ClaimantInfo> ciDict = new Dictionary<string, ClaimantInfo>();
public ClaimantUI()
{
InitializeComponent();
}
private void Saver()
{
if (ciDict.ContainsKey(LastNameTextBox.Text))
MessageBox.Show("You already have a claimaint with the same last name", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
else if ((FirstNameTextBox.Text != "") && (LastNameTextBox.Text != "")) // Check to see if there is a First & Last Name
if ((AddressTextBox.Text == "")) // If there is no address, just save the first/last name.
{
ciDict.Add(LastNameTextBox.Text, new ClaimantInfo(FirstNameTextBox.Text, LastNameTextBox.Text));
ClaimantListBox.Items.Add(LastNameTextBox.Text);
clearTextBoxes();
}
else // If there is an address save all info.
{
ciDict.Add(LastNameTextBox.Text, new ClaimantInfo(FirstNameTextBox.Text, LastNameTextBox.Text, AddressTextBox.Text, POBoxTextBox.Text, CityTextBox.Text, StateTextBox.Text, ZipTextBox.Text, PhoneTextBox.Text, CellTextBox.Text, EmailTextBox.Text));
ClaimantListBox.Items.Add(LastNameTextBox.Text);
clearTextBoxes();
}
else
MessageBox.Show("You must enter a first and last name.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
private void clearTextBoxes() // Used to empty textboxes before/after a load/save
{
FirstNameTextBox.Text = "";
LastNameTextBox.Text = "";
AddressTextBox.Text = "";
POBoxTextBox.Text = "";
CityTextBox.Text = "";
StateTextBox.Text = "";
ZipTextBox.Text = "";
PhoneTextBox.Text = "";
CellTextBox.Text = "";
EmailTextBox.Text = "";
}
private void unpacker(string name) // Takes values saved in ciDict and displays them in the text boxes.
{
FirstNameTextBox.Text = ciDict[name].firstName;
LastNameTextBox.Text = ciDict[name].lastName;
AddressTextBox.Text = ciDict[name].address;
POBoxTextBox.Text = ciDict[name].poBox;
CityTextBox.Text = ciDict[name].city;
StateTextBox.Text = ciDict[name].state;
PhoneTextBox.Text = ciDict[name].phone;
CellTextBox.Text = ciDict[name].cell;
EmailTextBox.Text = ciDict[name].email;
}
private void ClickAddButtonEvent(object sender, EventArgs e) // Checks textboxes to make sure they hold (correct) values, if they do then a new ClaimantInfo object is created and added to the list.
{
Saver();
}
private void DoubleClickLoadClaimantInfo(object sender, MouseEventArgs e)
{
string name = ClaimantListBox.SelectedItem.ToString();
foreach (KeyValuePair<string, ClaimantInfo> pair in ciDict)
{
try
{
if (pair.Key == name)
{
clearTextBoxes();
unpacker(name);
}
else if (pair.Key != name)
continue;
else
throw new IndexOutOfRangeException();
}
catch ( IndexOutOfRangeException )
{
MessageBox.Show("Whoops, that shouldn't have happened", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
private void ClickSaveButtonEvent(object sender, EventArgs e) // Same as add, only closes the window after saving.
{
Saver();
Close();
}
}Sorry for the long winded post, and thanks in advance again for any help you guys can offer. |
|
|
|
|
|
#8 | |||||
|
SEXY SHOELESS GOD OF WAR!
![]() Join Date: Jun 2005
Location: Wet west coast of Canada
Posts: 1,197
Rep Power: 5
![]() |
Re: Values being passed by reference?
Quote:
ClaimantInfo object. You want to find out the person's name? You use the Name property, like string name = ci.Name or some such. When you have a collection of ClaimantInfo objects, you will still be using the properties to get the details of the specific claimant.Quote:
ClaimantInfo objects. Right now, this might be a class that has a List<ClaimantInfo> full of claimants, but later on, it might be a class that makes queries to an underlying database. It really doesn't matter, as long as you have a consistent interface. Basically, you need to consider what you need to do, and it boils down to CRUD (I didn't pick the acronym). It stands for Create, Retrieve, Update, and Delete. These are the four types of functionality, and if you've got them, it doesn't matter how you're storing the data. You can be stuffing it in a database, writing it to a plain file, or whatever. Basically, they work as follows:Create adds a new record (duh). This will probably include checks for validity of values (the values make sense) as well as making sure certain fields are filled in. For example, it's reasonable to allow a blank email field, but probably not reasonable to allow blank name fields. Retrieve is your find functionality. You will probably have several methods here, each using different criteria (search by name, search by client ID, etc). Some will return a single record (if it's in the list) or nothing (null, empty object, whatever- it depends on your implementation) if no match is found (searching by ID is an example here, since IDs should be unique). Others will return an arbitrary number of records, such as when searching by name. Update gives you the ability to edit a record. It's pretty straightforward; as long as a record exists, you can modify it, and write it back. Note that certain fields on a record might be read-only; it should be possible to change the name or address associated with a claimant, but changing the ID value should probably not be allowed. Delete is about the easiest there is. If it's there, remove it; otherwise, do nothing. It's a good idea to have some kind of return value that indicates success or failure, but this goes for all the CRUD functions. Quote:
In fact, you may want an additional 'layer' here; this is the so-called 'three tier' approach. The three tiers, or layers, are the UI layer, which has your user interface, the business logic layer, and the data access layer. The data access one is responsible for maintaining the records on some form of persistent storage (disk files, database, whatever). The business logic layer sits between the DA and UI layers, and enforces certain rules. Thus, your UI code will call the BLL, rather than directly calling the methods in the DA tier. For example, you might not want to allow deletion of claimants with outstanding claims, so the business logic layer could enforce this. Using the three-tier approach will initially complicate the design and implementation, but it allows for a clearer separation of different kinds of functionality, which can make things easier in the long run. Let's say your boss comes up with some new policy he wants you to implement in the code; putting it into the business logic layer will allow you to leave the other two layers unchanged. Likewise, let's say you've originally got the DA tier implemented as flat file storage, and the boss says he wants it stored in a database instead. You can change this tier, but the other tiers don't care (or even know) about the changes. Quote:
Quote:
Of course, if you simply mean you're going to run this alongside the original system, with this new one tracking only contact info (and not any of the uber-critical claim stuff, then you don't need to worry as much about bugs. This is not to say you shouldn't test the code, but rather that if the consequences of failure are less severe, you can justify spending less time in testing.
__________________
And once again, Probability proves itself willing to sneak into a back alley and service Drama as would a copper-piece harlot. - Vaarsuvius, Order of the Stick |
|||||
|
|
|
|
|
#9 | |
|
Newbie
Join Date: Jul 2008
Location: Seattle, WA
Posts: 6
Rep Power: 0
![]() |
Re: Values being passed by reference?
Quote:
Anyways, thanks for your explanation about the differing layers approach. You explain these concepts pretty darn good. |
|
|
|
|
|
|
#10 |
|
Hobbyist Programmer
Join Date: Dec 2007
Location: Durban, South-Africa
Posts: 206
Rep Power: 1
![]() |
Re: Values being passed by reference?
Great example, thanks lectricpharaoh.
As allways, your explanation have inlightened more than just the thread starter, and I will now commence programming on the same topic discussed. ![]()
__________________
"Common sense is the collection of prejudices acquired by age eighteen." - Albert Einstein |
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|