I am really new to this, but I have 3 classes, and one of them uses the command line to ask the user to press 1 or 2 to answer questions, and then I am using a get; set; to collect that information in a different class.
I want to write that information into a text document. The problem is, my class where I am using StreamWriter isn't recognizing the words I used to define the information in the other class. Any help would be great. This is where I want the information to go.
StreamWriter writer = new StreamWriter(#"C:\Users\Dan\Documents\testText.txt");
writer.WriteLine("Passenger Information" //want the SeatSelection in here);
writer.Close();
The original information comes from here. Although in this code, it is recognizing the word "SeatSelection"? Why is it recognizing here but not above?
Console.Clear();
Console.WriteLine("Please select first or third class for your trip. ");
Console.WriteLine("1) First Class Compartment.");
Console.WriteLine("2) Third Class with Compartments.");
Console.WriteLine("3) Car C Open seating.");
Console.WriteLine("4) Car D Open seating.");
Console.WriteLine("5) Return to booking main menu.");
bool validInput = false;
do
{
validInput = true;
ConsoleKeyInfo key = Console.ReadKey();
switch (key.Key)
{
case ConsoleKey.D1:
case ConsoleKey.NumPad1:
seatClass.SeatSelection = "First Class";
FirstClass();
break;
case ConsoleKey.D2:
case ConsoleKey.NumPad2:
seatClass.SeatSelection = "Third Class Compartment";
ThirdClassCompartments();
break;
I'm not sure what your application structure is, but you want something along these lines:
var seatClass = UserInterface.GetSeatClass();
StreamWriter writer = new StreamWriter(#"C:\Users\Dan\Documents\testText.txt");
writer.WriteLine("Passenger Information" + seatClass.SeatSelection);
writer.Close();
...where the I have creatively inserted an imaginery UserInterface static class that returns a new instance of a SeatClass from it's GetSeatClass method:
public static class UserInterface
{
public static SeatClass GetSeatClass()
{
var seatClass = new SeatClass();
//code here that assigns values to seatClass
return seatClass;
}
}
I can give you a better answer with more information. Please don't hesitate to ask any questions. (I'm sorry I've used a static class when you're just starting out. I can explain if required.)
You can use this
var filePath = #"C:\Users\Dan\Documents\testText.txt";
var lines = new[] {"a line content"};
System.IO.File.AppendAllLines(filePath, lines);
Instead of StreamWriter stuff.
Related
My professor doesn't want all my code in one class. I am new to C# as well so I don't know how to make my code cohesive and have it abstract away any of the implementation details. Here is my code I have so far. I am trying to make multiple classes because this class has too many responsibilities and I don't know how to do that.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
namespace SvgGenerator
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter the name of the output file.");
string outputFile = Console.ReadLine() + ".svg";
Console.WriteLine("Do you want to manually enter the squares or read them from a file? Man or File?");
string fileRead = Console.ReadLine();
if (fileRead.Trim() == "Manually" || fileRead.Trim() == "manually" || fileRead.Trim() == "Man" || fileRead.Trim() == "man")
{
ManInput(outputFile);
}
if (fileRead.Trim() == "file" || fileRead.Trim() == "File")
{
FileInput(outputFile);
}
}
private static void FileInput(string outputFile)
{
Console.WriteLine("What is the name of the file?");
string titleFileName = Console.ReadLine();
StreamReader reader;
reader = new StreamReader(titleFileName);
string textFile = reader.ReadToEnd();
reader.Close();
string[] values = textFile.Split(',', '\n');
List<Square> squares = new List<Square>();
for (int i = 0; i < values.Length;)
{
int valueNumsX = int.Parse(values[i].Trim());
int valueNumsY = int.Parse(values[i + 1].Trim());
Square squareQ = new Square(Color.FromName(values[i + 2].Trim()), valueNumsX, valueNumsY);
squares.Add(squareQ);
if (i == values.Length - 3)
{
SvgBuilder svgBuilder = new SvgBuilder();
string SVG = svgBuilder.Build(squares);
FileCreator Myfilecreater = new FileCreator();
Myfilecreater.Create(outputFile, SVG);
}
i = i + 3;
}
}
private static void ManInput(string outputFile)
{
Console.WriteLine("How many squares do you want in your SVG file?");
string squareCount = Console.ReadLine();
int numSquareCount = Convert.ToInt32(squareCount);
Console.WriteLine("What are the colors of your squares?");
string[] squareColor = new string[numSquareCount];
List<Square> squares = new List<Square>();
for (int i = 0; i < numSquareCount; i++)
{
squareColor[i] = Console.ReadLine();
Square squareQ = new Square(Color.FromName(squareColor[i]), i*4, 0, 200);
squares.Add(squareQ);
if (i == numSquareCount - 1)
{
SvgBuilder svgBuilder = new SvgBuilder();
string SVG = svgBuilder.Build(squares);
FileCreator Myfilecreater = new FileCreator();
Myfilecreater.Create(outputFile, SVG);
}
}
}
}
}`
First of all you should separate classes or methods handling input from classes handling output. If is also typically a poor idea to mix UI from the functional parts, even if the the UI is a console for this case.
I would suggest using the following methods:
private static IEnumerable<Square> ReadSquaresFromFile(string filePath)
private static IEnumerable<Square> ReadSquaresFromConsole()
private static WriteToFile(IEnumerable<Square> squares, string filePath)
For such a simple program procedural programming should be just fine. You do not have to use object. But if you want to, you could for example create a interface like:
public interface ISquareSource(){
IEnumerable<Square> Get();
}
With a file-implementation, console-implementation etc.
Note that I have used string filePath as the file source/destination. If you ever write a library or API, please ensure you have an overlay that takes a stream. It is super annoying to have some data in memory and being forced to write it to a temporary file just because some developer only imagined reading from actual files.
You could also consider using switch statements for handling input, for example
switch(fileRead.Trim().ToLower()){
case "manually":
...
break;
case "file":
...
break;
default:
Console.WriteLine("Invalid input, expected 'manually' or 'file'
break;
Cohesion is the idea that code that does the same thing belongs together, and code that doesn't do the same thing doesn't belong together.
So, let's consider the FileInput function. At a glance, we can see that it does the following:
Prompts the user for a file name to load.
Opens the file.
Reads all of its content into memory.
Closes the file.
Parses the file content into an array of strings.
For each item in the array:
Parses some integral values.
Creates a Square object from those values.
If the index of the current item is equal to the length of the array less 3:
Instantiates a new SvgBuilder.
Invokes its Build method.
Instantiates a new FileCreator.
Invokes its Create method.
There's a lot going on here. Essentially, there are three separate kinds of work going on here that could be broken out into individual functions:
User input (arguably this could be part of the main function).
Call file deserialization function (reads the input file into memory and returns it as an array of strings).
Call main processing function (iterates over the array)
Performs calculations and creates of Square object.
If index of the current item is array length less 3:
Call Build SVG File function
Call File Creation function.
This is what your instructor is getting at.
I'm trying to make it so when the given answer is neither 1 nor 2 the message "Please enter a valid answer." shows up and it goes back to the question.
Here's my code:
Coloration(ConsoleColor.DarkMagenta, "What do you want to do? [1/2]");
Console.WriteLine("1. Draw");
Console.WriteLine("2. Stay");
int i = 0;
string input1 = Console.ReadLine();
// If answer is not 2, go through this, if answer is 2 continue
if (input1 != "2")
{
// If answer is 1 add 1 to i
if (input1 == "1")
{
i++;
}
// If answer is neither 1 nor 2; go back to question
if (input1 != "1" || input1 != "2")
{
Coloration(ConsoleColor.Red, "Please enter a valid answer.");
}
}
You want something more like this...
Console.WriteLine("What do you want to do? [1/2]");
Console.WriteLine("1. Draw");
Console.WriteLine("2. Stay");
int userChoice = 0;
bool validInput = false;
while (!validInput)
{
Console.WriteLine();
Console.WriteLine("Enter choice [1/2]...");
string input = Console.ReadLine();
string trimmedInput = input.Trim();
if (trimmedInput == "1" || trimmedInput == "2")
{
validInput = true;
userChoice = Int32.Parse(trimmedInput);
}
}
// We leave the while loop here once validInput == true
// Now take action based on userChoice
Console.WriteLine("You chose " + userChoice);
Console.ReadLine();
As others have noted, you need to keep watching user input. You have your answer but I wanted to also introduce you to the concept of a Read Eval Print Loop (AKA a REPL). Your current solution is not going to scale well. Take a look at the following implementation of your desired UI:
using System;
using System.Collections.Generic;
namespace MyProgram
{
class Program
{
// Don't actually use inner classes. This is just for demonstration purposes.
class Command
{
public Command(string description, Action action)
{
this.Description = description;
this.Action = action;
}
public string Description { get; private set; }
public Action Action { get; private set; }
}
static void Main(string[] args)
{
// Create a dictionary of commands, mapped to the input that evokes each command.
var availableCommands = new Dictionary<string, Command>();
// Note that since we are storing the descriptions / commands in one place, it makes
// changing a description or adding/modifying a command trivial. Want "Draw" to be invoked
// by "d" instead of "1"? Change it here and you're done.
availableCommands.Add("1", new Command("Draw", Draw));
availableCommands.Add("2", new Command("Stay", Stay));
// This command demonstrates how to use a lambda as an action if you so desire.
availableCommands.Add("3", new Command("Exit", () => System.Environment.Exit(1)));
// Build command list string
var cmdList = string.Join('/', availableCommands.Keys);
// Display welcome message
Coloration(ConsoleColor.DarkMagenta, $"What do you want to do? [{cmdList}]");
// Show user initial list of commands
DisplayAvailableCommands(availableCommands);
// Read Eval Print Loop (REPL).
while (true)
{
var userInput = Console.ReadLine();
// If the user entered a valid command, execute it.
if (availableCommands.ContainsKey(userInput))
{
availableCommands[userInput].Action();
// If you want the user to be able to perform additional actions after their initial successful
// action, don't return here.
return;
}
// Otherwise, let them know they didn't enter a valid command and show them a list of commands
else
{
Coloration(ConsoleColor.Red, "Please enter a valid answer.");
DisplayAvailableCommands(availableCommands);
}
}
}
// I'm just assuming your coloration method looks something like this...
static void Coloration(ConsoleColor color, string message)
{
// Keep track of original color so we can set it again.
var originalColor = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(message);
Console.ForegroundColor = originalColor;
}
static void DisplayAvailableCommands(Dictionary<string, Command> commands)
{
// We always want a line above the commands
Console.WriteLine("Available commands:");
foreach (string key in commands.Keys)
{
var command = commands[key];
Console.WriteLine($"{key}. {command.Description}");
}
}
static void Draw()
{
Coloration(ConsoleColor.DarkGreen, "You chose to draw!");
}
static void Stay()
{
Coloration(ConsoleColor.DarkGreen, "You chose to stay!");
}
}
}
Adding new commands will be a breeze. And you can further refactor this to even smaller single-purpose methods. Each command could have its' own class in the event that your commands become more complicated (and in a real app, they will. Trust me). You can refactor the REPL to its' own class/method as well (take a look at this implementation I did a while back for example).
Anyway, this is my $.02 on a more correct way you could build a user interface of this nature. Your current solution is going to work, but it's not going to be fun to work with long-term. Hopefully this helps.
use a while loop to continuously ask for input if not valid, then check the input with a switch statement
bool flag = false;
while(!flag)
{
switch(input1)
{
case "1":
flag = true;
break;
case "2":
flag = true;
break;
default:
Coloration(ConsoleColor.Red, "Please enter a valid answer.");
break;
}
}
Working on an assignment and need a little help. I'm trying to have the user input their list of fun places to visit and as they add the places I want to save it to a list and then give them the option to view the entire list of places. I'm having trouble saving the objects to a list:
using System;
using static System.Console;
namespace Assignment_4
{
class Program
{
static void Main()
{
Test.DisplayInstructions();
Test.AddPlace();
WriteLine("Would you like to add another place, Y/N?");
var addAnother = ReadKey().KeyChar;;
switch (char.ToLower(addAnother))
{
case 'y':
Test.AddPlace();
break;
case 'n':
break;
default:
break;
}
WriteLine("Would you like to see your list of fun places? Y/N");
var reviewList = ReadKey().KeyChar;
switch (char.ToLower(reviewList))
{
case 'y':
WriteLine(Test.funPlacesList);
break;
case 'n':
break;
default:
break;
}
}
}
}
This is the test I run to check the program:
using System;
using System.Collections.Generic;
using static System.Console;
namespace Assignment_4
{
public class Test
{
internal static void DisplayInstructions()
{
WriteLine("Welcome to my application: \n This app will you write about your favorite places \n" +
"To begin we'll need you to provide the information as prompted \n" +
"The app will then take that info and display it back to you. Showing you your favorite place!");
}
public static List<FunPlace> funPlacesList = new List<FunPlace>();
internal static void AddPlace()
{
FunPlace place = new FunPlace();
WriteLine("What is the name of this place?");
place.PlaceName = ReadLine();
WriteLine("Where is it located?");
place.PlaceAddress = ReadLine();
WriteLine("What kinds of fun things can you do there?");
place.PlaceDescription = ReadLine();
WriteLine("Any additional Comments to add?");
place.PlaceComments = ReadLine();
WriteLine("Provide a rating from 0.0 - 5.0.");
place.PlaceRating = float.Parse(ReadLine());
funPlacesList.Add(place);
ReadKey();
}
}
}
It looks to me like you're adding to the list just fine. You can't just Console.WriteLine(funPlacesList) though, all you're going to get is the name of the class. You need to loop over the collection and write out each entry individually.
foreach(var funPlace in Test.funPlacesList)
{
WriteLine(funPlace.PlaceName);
WriteLine(funPlace.PlaceAddress);
// etc...
}
foreach (FunPlace place in Test.funPlacesList)
{
Console.Writeline(place.PlaceName);
}
Is this what you were looking for? foreach can be used to loop through a collection of objects(list, array etc.) and reference each one inside the foreach block as place in this example.
For the record, I highly recommend coming up with another name for your Test class. Good names are a crucial long-term investment in the ease of understanding your code.
I have been trying to work this concept out for days. I have a console program with 3 classes :
1) Main program
2) Create Login screen
3) Actual Login screen
Here is the code:
class Program
{
static void Main(string[] args)
{
//Instantiate Main Menu
Menu myMenu = new Menu();
myMenu.mainMenu();
//testing global values
Login myLogin = new Login();
Console.Write("The new Login is ");
Console.WriteLine(myLogin.newLogin);
//Pause
Console.WriteLine("Press Any Key...");
Console.ReadLine();
}
}
class Menu
{
public void mainMenu()
{
// [create menu that prints choices on the screen]
start:
Console.Clear();
Console.WriteLine("PLEASE CHOOSE AN OPTION");
Console.WriteLine();
Console.WriteLine("[1] LOGIN \n[2] CREATE LOGIN");
Console.WriteLine();
Console.Write("> ");
string menuChoice = Console.ReadLine();
// [switch/case for Main Menu]
switch (menuChoice)
{
case "1":
Console.WriteLine("You chose 1");
break;
case "2":
// [instantiate createLogin]
Login myLogin = new Login();
myLogin.createLogin();
Console.WriteLine(myLogin.newLogin);
break;
default:
Console.WriteLine("INVALID OPTION... TRY AGAIN.");
System.Threading.Thread.Sleep(5000);
goto start;
}
}
class Login
{
// [empty containers for newLogin & newPass]
public string newLogin { get; set; }
public string newPass { get; set; }
public void createLogin()
{
// [display new login screen & assign newLogin & newPass]
Console.Clear();
Console.WriteLine("CREATE NEW LOGIN");
Console.WriteLine();
Console.Write("LOGIN: ");
newLogin = Console.ReadLine();
Console.Write("PASSWORD: ");
newPass = Console.ReadLine();
// [instantiate & return to main menu]
Menu myMenu = new Menu();
myMenu.mainMenu();
}
}
Now when I try to output the code from main program the value is null. Do I have the wrong structure here? I dont really need to store more than one login and pass (for now) so an array is not needed at this point, but im sure ill learn that down the road. I am self learning so I am trying to keep things as simple as I can until I nail the concept.
I want to store the newLogin and newPass and print the values in main. Please help.
Thanks
Mike
In this code:
.....
case "2":
// [instantiate createLogin]
Login myLogin = new Login();
Console.WriteLine(myLogin.newLogin); //WRITE TO CONSOLE
myLogin.createLogin(); // QUERY AFTER WRITE
......
You first write an emtpy login, and after only query for it. So it's "normal" that you don't see any login information on the scree, as it printed before it was intitialized and after never printed again.
To resolve this, just invert function calls:
.....
case "2":
// [instantiate createLogin]
Login myLogin = new Login();
myLogin.createLogin(); // QUERY
Console.WriteLine(myLogin.newLogin); //WRITE TO CONSOLE
...
EDIT
If you want to operate over Login, one of possible solutions is:
class Menu
{
private Login _login = new Login(); // EVERYWHERE IN MENU USE THIS FIELD, THE SAME INSTANCE
.....
case "2":
// [instantiate createLogin]
//Login myLogin = new Login(); NO MORE NEW INSTANCE BUT USE ALREADY AVAILABLE ONE
_login.createLogin(); // QUERY
Console.WriteLine(_login .newLogin); //WRITE TO CONSOLE
...
}
Are you sure it's showing up as null in Main()? You're also printing myLogin.newLogin in your Menu switch/case. When you print it in your Menu, you're printing it before calling myLogin.createLogin() so it should be null, but by the time it gets back to Main() it should be set correctly.
I found an answer. It may not be the best, but it works. Once I corrected the Query/Write problem (listed above) I started thinking more about instances and realized that BY CREATING THE NEW INSTANCE I AM IN FACT CLEARING THE DATA. Sorry for the caps but my intent is to help some other self learner.
Ok so in theory I want to keep the createLogin method once its called in the case. How do I keep it? I made the createLogin, newLogin, and newPass classes static so that the value can be assigned and the class can then be called from main. By calling it without instantiating it, it keeps the value assigned.
class Login
{
// [empty containers for newLogin & newPass]
public static string newLogin { get; set; }
public static string newPass { get; set; }
public static void createLogin()
{
// [display new login screen & assign values]
Console.Clear();
Console.WriteLine("CREATE NEW LOGIN");
Console.WriteLine();
Console.Write("LOGIN: ");
newLogin = Console.ReadLine();
Console.Write("PASSWORD: ");
newPass = Console.ReadLine();
// [instantiate & return to main menu]
//Menu myMenu = new Menu();
//myMenu.mainMenu();
}
and in main:
//testing global values
Console.Write("The new Login is ");
Console.WriteLine(Login.newLogin);
This works, thanks everyone for contributing. It was with others comments that I was able to figure this one out. Hope my solution is clear for someone else. Peace.
I been having trouble trying to figure this out. When I think I have it I get told no. Here is a picture of it.
I am working on the save button. Now after the user adds the first name, last name and job title they can save it. If a user loads the file and it comes up in the listbox, that person should be able to click on the name and then hit the edit button and they should be able to edit it. I have code, but I did get inform it looked wackey and the string should have the first name, last name and job title.
It is getting me really confused as I am learning C#. I know how to use savefiledialog but I am not allowed to use it on this one. Here is what I am suppose to be doing:
When the user clicks the “Save” button, write the selected record to
the file specified in txtFilePath (absolute path not relative) without
truncating the values currently inside.
I am still working on my code since I got told that it will be better file writes records in a group of three strings. But this is the code I have right now.
private void Save_Click(object sender, EventArgs e)
{
string path = txtFilePath.Text;
if (File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
foreach (Employee employee in employeeList.Items)
sw.WriteLine(employee);
}
}
else
try
{
StreamWriter sw = File.AppendText(path);
foreach (var item in employeeList.Items)
sw.WriteLine(item.ToString());
}
catch
{
MessageBox.Show("Please enter something in");
}
Now I can not use save or open file dialog. The user should be able to open any file on the C,E,F drive or where it is. I was also told it should be obj.Also the program should handle and exceptions that arise.
I know this might be a noobie question but my mind is stuck as I am still learning how to code with C#. Now I have been searching and reading. But I am not finding something to help me understand how to have all this into 1 code. If someone might be able to help or even point to a better web site I would appreciate it.
There are many, many ways to store data in a file. This code demonstrates 4 methods that are pretty easy to use. But the point is that you should probably be splitting up your data into separate pieces rather than storing them as one long string.
public class MyPublicData
{
public int id;
public string value;
}
[Serializable()]
class MyEncapsulatedData
{
private DateTime created;
private int length;
public MyEncapsulatedData(int length)
{
created = DateTime.Now;
this.length = length;
}
public DateTime ExpirationDate
{
get { return created.AddDays(length); }
}
}
class Program
{
static void Main(string[] args)
{
string testpath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestFile");
// Method 1: Automatic XML serialization
// Requires that the type being serialized and all its serializable members are public
System.Xml.Serialization.XmlSerializer xs =
new System.Xml.Serialization.XmlSerializer(typeof(MyPublicData));
MyPublicData o1 = new MyPublicData() {id = 3141, value = "a test object"};
MyEncapsulatedData o2 = new MyEncapsulatedData(7);
using (System.IO.StreamWriter w = new System.IO.StreamWriter(testpath + ".xml"))
{
xs.Serialize(w, o1);
}
// Method 2: Manual XML serialization
System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(testpath + "1.xml");
xw.WriteStartElement("MyPublicData");
xw.WriteStartAttribute("id");
xw.WriteValue(o1.id);
xw.WriteEndAttribute();
xw.WriteAttributeString("value", o1.value);
xw.WriteEndElement();
xw.Close();
// Method 3: Automatic binary serialization
// Requires that the type being serialized be marked with the "Serializable" attribute
using (System.IO.FileStream f = new System.IO.FileStream(testpath + ".bin", System.IO.FileMode.Create))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bf.Serialize(f, o2);
}
// Demonstrate how automatic binary deserialization works
// and prove that it handles objects with private members
using (System.IO.FileStream f = new System.IO.FileStream(testpath + ".bin", System.IO.FileMode.Open))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
MyEncapsulatedData o3 = (MyEncapsulatedData)bf.Deserialize(f);
Console.WriteLine(o3.ExpirationDate.ToString());
}
// Method 4: Manual binary serialization
using (System.IO.FileStream f = new System.IO.FileStream(testpath + "1.bin", System.IO.FileMode.Create))
{
using (System.IO.BinaryWriter w = new System.IO.BinaryWriter(f))
{
w.Write(o1.id);
w.Write(o1.value);
}
}
// Demonstrate how manual binary deserialization works
using (System.IO.FileStream f = new System.IO.FileStream(testpath + "1.bin", System.IO.FileMode.Open))
{
using (System.IO.BinaryReader r = new System.IO.BinaryReader(f))
{
MyPublicData o4 = new MyPublicData() { id = r.ReadInt32(), value = r.ReadString() };
Console.WriteLine("{0}: {1}", o4.id, o4.value);
}
}
}
}
As you are writing the employee objects with WriteLine, the underlying ToString() is being invoked. What you have to do first is to customize that ToString() methods to fit your needs, in this way:
public class Employee
{
public string FirstName;
public string LastName;
public string JobTitle;
// all other declarations here
...........
// Override ToString()
public override string ToString()
{
return string.Format("'{0}', '{1}', '{2}'", this.FirstName, this.LastName, this.JobTitle);
}
}
This way, your writing code still keeps clean and readable.
By the way, there is not a reverse equivalent of ToSTring, but to follow .Net standards, I suggest you to implement an Employee's method like:
public static Employee Parse(string)
{
// your code here, return a new Employee object
}
You have to determine a way of saving that suits your needs. A simple way to store this info could be CSV:
"Firstname1","Lastname 1", "Jobtitle1"
" Firstname2", "Lastname2","Jobtitle2 "
As you can see, data won't be truncated, since the delimiter " is used to determine string boundaries.
As shown in this question, using CsvHelper might be an option. But given this is homework and the constraints therein, you might have to create this method yourself. You could put this in Employee (or make it override ToString()) that does something along those lines:
public String GetAsCSV(String firstName, String lastName, String jobTitle)
{
return String.Format("\"{0}\",\"{1}\",\"{2}\"", firstName, lastName, jobTitle);
}
I'll leave the way how to read the data back in as an exercise to you. ;-)