Create a Generic List Class in C# - c#

I am working on some university material and I Have the following question
Design and implement a collection class dedicated to storing all objects of company
documents as per the class hierarchy, inheriting from any .NET suitable collection class (e.g.
the ArrayList class, or generic List). The collection is to implement a single method
Page 2 of 5
GetDataOfAll() returning a string that concatenates data of all objects with suitable
separators. (The method will later be used to display data in a suitable output placeholder)
I wrote this:
class MainList : List<Document>
{
public string GetDataFormAll()
{
string text = null;
foreach (Document data in MainList)
{
text += data.GetData() + "\n";
}
return text;
}
}
Is this the correct way to implement this?
foreach (Document data in MainList)
This is giving me an error it is telling me that MainList is not of the correct type. How am I to implement this, please.

You need to loop over the Documents in your own (this) collection. Since a List<T> is Enumerable<T>, you can simply write a foreach loop over the this:
public string GetDataFormAll() {
string text = null;
foreach (Document data in this) {
text += data.GetData() + "\n";
}
return text;
}
(Yes, I code like an Egyptian)

Just another and shorter way, not necessarily better
public string GetDataFormAll()
{
return string.Join("\n", this.Select(d => d.GetData());
}
And of course you can shorten it even more with .Select(GetData) syntax.
This will not add a \n after the last element. Depends on what you want.

Related

Save dictionary to text file (C#)

I want to save a dictionary to a text file in Unity3D (using C#). The dictionary contains GameObjects as keys which corresponds to lists of Vector2's.
This is my code:
public void SaveLevel(string LevelName)
{
using (StreamWriter file = new StreamWriter (LevelName+".txt"))
{
foreach (GameObject entity in levelStruct.Keys)
{
file.WriteLine (entity.ToString () + ": " + levelStruct[entity].ToString());
}
}
}
Which produces the example file:
wall (UnityEngine.GameObject): System.Collections.Generic.List`1[UnityEngine.Vector2]
rat (UnityEngine.GameObject): System.Collections.Generic.List`1[UnityEngine.Vector2]
dwarf (UnityEngine.GameObject): System.Collections.Generic.List`1[UnityEngine.Vector2]
floor (UnityEngine.GameObject): System.Collections.Generic.List`1[UnityEngine.Vector2]
My issue is that the file needs to contain the actual items in the Vector2 list, not the list itself:
System.Collections.Generic.List1[UnityEngine.Vector2]
I want the above line to look something like this:
System.Collections.Generic.List1[(0,2),(3,1),(4,3)]
How would i accomplish this?
you can create a varaible of the specific type and get the properties you need
public void SaveLevel(string LevelName)
{
string res;
using (StreamWriter file = new StreamWriter (LevelName+".txt"))
{
foreach (GameObject entity in levelStruct.Keys)
{
foreach(Vector2 v in levelStruct[entity])){
res = " "+"("+v.x+"."+v.y+")";
}
file.WriteLine (entity.ToString () + ": " + res);
}
}
}
I have not used Unity before, so apologies if I have misunderstood. If Vector2 is a custom class that you have created, then you can override the default ToString() method, so that it spits out whichever string you need. Alternatively, you need to access whichever property it is that you're trying to write out from the value in your dictionary. E.g (assuming Foo is your property).
file.WriteLine (entity.ToString() + ": " + levelStruct[entity].Foo.ToString());
I think you should use serializing and deserializing to save and recover your objects, because when you use ToString() function return just the name of object in most of times. I suggest you to google it but there is some resources:
http://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part
https://msdn.microsoft.com/en-us/library/58a18dwa(v=vs.110).aspx
You are writing objects. When you call a ToString() on an object, you get the name of the object type. Instead, you need to do one of two things:
#1: If you really want to write using CSV as your code suggests:
Object.Property.ToString();
In the case of a GameObject, you might need to use reflection to do this
using System.Reflection;
foreach(PropertyInfo p in typeof(UnityEngine.GameObject).GHetProperties()){
Console.WriteLine(p.Name+","+p.GetType().ToString());
}
//repeat as necessary with fields and methods and ... as needed using System.Reflection
The second item is a list of vectors - same thing. For that, you'd use
foreach(UnityEngine.Vector2 vect in levelStruct[entity])){
//write ToString();
}
#2: Learn how to serialize object to XML or Json and save then that way:
https://www.google.com/search?q=c-sharp+how+to+serialize+objects+into+json&ie=&oe=
Good luck - if you get stuck on something specific just ask.

My function can't output string array

I think the answer to this is pretty obvious, but it's friday so my brain isn't functioning entirely.
I'm working in an asp.net mvc application where I get the id of a selected row. Every time I click a row the id of that row is send to my controller.
What I want to do is output the id just as regular plain text.
This isn't too hard, I use this code:
public string GetInformation(int id)
{
return id.toString();
}
But the user can select multiple rows at once, what happens now is that the id just changes from the first selected row to the second one.
What I want is both the id's to be returned, separated by a comma.
This is what I tried:
public string[] GetInformation(int id)
{
List<string> oud = new List<string>();
oud.Add(id.ToString());
return oud.ToArray();
}
But then it just shows System.String[] as output.
Ok, I'm going to prefix this by saying that I think using sessions for state is generally a bad idea. However, it's an easy way to show state management and retaining the list between requests.
Update your function to this:
public string[] GetInformation(int id)
{
var list = Session["oud"] as List<string>;
if (list == null)
{
list = new List<string>();
Session["oud"] = list;
}
list.Add(id.ToString());
return list.ToArray();
}
Each time you call this method it will get the list from session (user state management). If the list returned from session is null (it's not actually there) we create a new list of strings and add it to the session.
To test this I put the following in an index action of an MVC controller before return View(). When I refresh the page I can see the random id being appended to the list. The same will apply if you make a call from the client.
Random r = new Random();
GetInformation(r.Next(1, 10));
Hope this helps!
Update
Iterating over an array in razor view and printing each item
In your razor view, if you have added this array to your model as MyIds, you could use the following:
#foreach (var id in Model.MyIds)
{
<p>#Html.Raw(id)</p>
}
Update 2
I'm going to make this very easy for you:
public string GetInformation(int id)
{
var list = Session["oud"] as List<string>;
if (list == null)
{
list = new List<string>();
Session["oud"] = list;
}
list.Add(id.ToString());
return string.Join(", ", list);
}
The function now returns a comma separated list of ids that you have passed through.
But then it just shows System.String[] as output.
Well, you're not showing us how you actually output anything. But presumably you're getting a value:
var output = GetInformation(input);
and then trying to write output directly as a string. However, string[] is an array and doesn't have a default string representation. All objects in .NET which don't have a .ToString() implementation inherit their implementation from System.Object, which defaults to outputting the type name.
Instead of outputting the object itself, which is semantically something like this:
Write(output)
(again, semantically, since we don't know how you're actually outputting it, so consider this pseudo-code)
Loop over it for your output:
foreach (var value in output)
Write(value)
You'll have to handle formatting (new lines, delimiters, etc.) for however you want to display the values as a UI concern. But the point, basically, is that you need to loop over your values and output them individually rather than as one big array.
There are shortcuts which will loop over it for you, if you'd like. For example:
Write(string.Join(",", output))
That would "join" all of the strings in output for you, using "," as a delimiter.
Edit: Another problem that you seem to be experiencing is that your method only ever returns a new list of exactly one object:
public string[] GetInformation(int id)
{
List<string> oud = new List<string>();
oud.Add(id.ToString());
return oud.ToArray();
}
This method shouldn't really have the responsibility of maintaining the list. This is because the method itself is stateless, all it really does is convert an integer value to a string. (Which you don't really need a method for, but whatever.) Consuming code should maintain state.
So keep the method as-is:
public string GetInformation(int id)
{
return id.toString();
}
And have the consuming code maintain the collection of values. Something like this:
var output = GetInformation(input);
myOutputs.Add(output);
Where is myOutputs defined? Well, where that state is maintained depends on a lot of things. This is a web application, so state can be an interesting thing. There are many places to maintain it:
Within the consuming method as a method-level variable
Within the consuming class as a class-level value
In session state
In a database
etc.
The overall flow of the logic and the application is going to govern this. For example, if a single instance of the class is maintaining the entire lifetime of this process then you would put it in a class-level value. However, if the value needs to persist across different page requests then you might want to put it in Session State instead. You have a number of options for where to maintain this collection of strings.
Try this:
PSEUDO CODE
public string[] GetInformation(int id)
{
List<string> oud = new List<string>();
oud.Add(id.ToString());
return oud.ToArray();
}
var myResult = GetInformation(1);
Console.WriteLine(string.Join(", ", myResult);
It's more nice to return unformatted data and format it when needed.
public int[] GetInformation(int id)
{
List<int> oud = new List<int>();
oud.Add(id);
return oud.ToArray();
}
var myResult = GetInformation(1);
Console.WriteLine(string.Join(", ", myResult);

List.First not returning anything, while there is something

I'm making something like an objective list with C#.
I got it working and all, able to draw the objective list inside Unity, and make new objectives with them. I had an idea on how to delete something, I used System.Linq and the List.First function to search for it. But it does not seem to work.
This is the 'Finishing and Creating Objectives' code.
public void NewObjective(string objectiveText, string objName)
{
objectives.Add(new Objective(objectiveText, objName)); //Add to the list Objective.
middleText("New Objective: " + objectiveText); //Display text on screen.
}
public void FinishObjective(string shortObj)
{
var value = objectives.First(x => x.ObjectiveName.Contains( shortObj ));
string completedTask = value.objectiveDescription;
middleText("Completed Objective: " + completedTask); //Display text on screen.
objectives.Remove(value); //Remove value. (Which doesn't find anything for some reason, so it can't delete anything from the list.)
}
And then in another class, I have it make a new objective, like this.
GameController.gameController
.GetComponent<GameController>()
.NewObjective("Foobar.", "foo"); //First is the Quest description,
and the second is the name for easy deletion.
(I've also included a objectiveID method but I've omitted for ease.)
In that same class, when the player completes something, I have this.
GameController.gameController
.GetComponent<GameController>()
.FinishObjective("foo"); //This has two possible methods,
the object ID (if defined) or the name of the objective.
What is going on, what am I doing wrong and what can I do to fix this?
Thanks for the help.
Edit:
There is no actual error. It's just that it doesn't find anything, while there is something. Objectives is just easily defined as List objectives = new List(); inside the class.
This is the objectives class:
public class Objective : MonoBehaviour
{
public string objectiveDescription;
public string ObjectiveName;
public int ObjectiveID;
public Objective(string objective, string objectiveName)
{
objectiveDescription = objective;
ObjectiveName = objectiveName;
}
public Objective(string objective, string objectiveName, int objectiveID)
{
objectiveDescription = objective;
ObjectiveName = objectiveName;
ObjectiveID = objectiveID;
}
}
You may need to implement something like IEquatable inside your Objective class. You are asking the computer to check if ObjectiveName contains shortObj but it does not know how to make this comparison. is ObjectiveName a string? List of strings? a(n) Objective class? If it's a list of strings that we are calling "objective names" then the expected element would be a string (something like shortObj.ObjectiveName assuming that's a string). If ObjectiveName is a list of Objective classes and you are asking if this list contains a specific Objective element called shortObj, then you'll need to implement IEquatable into your Objective class.
EDIT
In light of recent comments, try something like:
var value = objectives.AsEnumerable().Where(x => x.ObjectiveName == shortObj).FirstOrDefault();

C# User Defined CSV Mapping to POCO

I have a system that reads input data from a Serial/UDP/TCP source. The input data is simply a CSV of different datattypes (e.g. DateTime, double, int, string). An example string might be:
2012/03/23 12:00:00,1.000,23,information,1.234
I currently have (untested) code that allows the user to specify which value in the CSV list goes to which property of a POCO.
So in the above example, i would have a object like so:
public class InputData
{
public DateTime Timestamp{get;set;}
public double Distance{get;set;}
public int Metres{get;set;}
public string Description{get;set;}
public double Height{get;set;}
}
Now in this class, i have a method to parse a CSV string and populate the properties. This method also requires "Mapping" information, as there is no guarantee which order the CSV data will arrive in - it is up to the user to define the correct order.
This is my Mapping class:
//This general class handles mapping CSV to objects
public class CSVMapping
{
//A dictionary holding Property Names (Key) and CSV indexes (Value)
//0 Based index
public IDictionary<string, int> Mapping { get; set; }
}
Now my method ParseCSV():
//use reflection to parse the CSV survey input
public bool ParseCSV(string pCSV, CSVMapping pMapping)
{
if (pMapping == null) return false;
else
{
Type t = this.GetType();
IList<PropertyInfo> properties = t.GetProperties();
//Split the CSV values
string[] values = pCSV.Split(new char[1] { ',' });
//for each property set its value from the CSV
foreach (PropertyInfo prop in properties)
{
if (pMapping.Mapping.Keys.Contains(prop.Name))
{
if (prop.GetType() == typeof(DateTime))
{
if (pMapping.Mapping[prop.Name] >= 0 && pMapping.Mapping[prop.Name] < values.Length)
{
DateTime tmp;
DateTime.TryParse(values[pMapping.Mapping[prop.Name]], out tmp);
prop.SetValue(this, tmp, null);
}
}
else if (prop.GetType() == typeof(short))
{
if (pMapping.Mapping[prop.Name] >= 0 && pMapping.Mapping[prop.Name] < values.Length)
{
double tmp;
double.TryParse(values[pMapping.Mapping[prop.Name]], out tmp);
prop.SetValue(this, Convert.ToInt16(tmp), null);
}
}
else if (prop.GetType() == typeof(double))
{
if (pMapping.Mapping[prop.Name] >= 0 && pMapping.Mapping[prop.Name] < values.Length)
{
double tmp;
double.TryParse(values[pMapping.Mapping[prop.Name]], out tmp);
prop.SetValue(this, tmp, null);
}
}
else if (prop.GetType() == typeof(string))
{
if (pMapping.Mapping[prop.Name] >= 0 && pMapping.Mapping[prop.Name] < values.Length)
{
prop.SetValue(this, values[pMapping.Mapping[prop.Name]], null);
}
}
}
}
return true;
}
}
Now for my question:
I potentially have several classes that will require this functionality. Would it be beneficial to implement a generic class or an extension class to do the parsing for me? Is my method a sound way to parse CSV data and popluate my object - or is there a better way to do this?
I have read other questions on dynamically parsing CSV, but all deal with the order being known before runtime, whereas i require the user to define the order.
OleDB is great at parsing CSV data and you don't have to use reflection for it. Here's the main idea for mapping with OleDB classes:
User defines a mapping (using delegate, fluent interface or something) and it gets into the Dictionary in your Mapper class.
Parser creates a DataTable and inserts columns from mapper
Parser creates OleDbConnection, Adapter, Command and fills dataTable from CSV file in correct types.
Parser extracts IDataRecords from DataTable and your Mapper needs to map from IDataRecord to objects. For guidance on record-to-object mapping I'd recommend reading sources of ORM mappers like Dapper.NET, Massive, PetaPoco.
OleDb CSV parsing: Load csv into oleDB and force all inferred datatypes to string
UPDATE
Since there's only one string, it goes without saying that using easiest and simplest approach is better. So, for the questions:
Implement generic class - if there's no need to further advance parsing (no more string, no more constraints/features in the future), I'd go for a static class that takes object, string and mapping information. It'd have almost the same look as yours does right now. Here's somewhat modified version (may not compile, but should reflect the general idea):
public static class CSVParser
{
public static void FillPOCO(object poco, string csvData, CSVMapping mapping)
{
PropertyInfo[] relevantProperties = poco.GetType().GetProperties().Where(x => mapping.Mapping.Keys.Contains(x)).ToArray();
string[] dataStrings = csvData.Split(',');
foreach (PropertyInfo property in relevantProperties)
SetPropertyValue(poco, property, dataStrings[mapping.Mapping[property.Name]]);
}
private static void SetPropertyValue(object poco, PropertyInfo property, string value)
{
// .. here goes code to change type to the necessary one ..
property.SetValue(poco, value);
}
}
Regarding the string to typed value conversion - there's Convert.ChangeType method that handles most of the cases. There's particular problem with boolean variables (when it's given "0" instead of "false") though.
As for data population - though reflection is said to be slow, for single objects and seldom usages it should suffice since it's easy and simple. Usual methods for dealing with problem of poco population are: run-time conversion method creation (that uses reflection to be initialized and then is compiled and called like any other method) - usually implemented using DynamicMethod, Expression Trees and similar - there's plenty of topic here on so; usage of dynamic objects (available since C# 4.0) - where to assign/get variable you don't need to declare it; use available libraries on the market (usually from the ORM systems, since they rely heavily on data-to-object conversion).
Personally, I'd measure if reflection is suitable for my performance needs and would progress forward pass the problem.
I would 100% agree with #Dimitriy on this one, as I've wrote 5-10 CSV parsers over the past few weeks.
Edit: (Note this requires saving the text to a temporary file using something like Path.GetTempFile(), but it will allow the flexibility you desire)
The argument of using a DataTable would be best as when the connection string is used properly - using Extended Properties='true;FMT=Delimited;HDR=Yes', it will go into a DataTable and the column headings (which would help you in this case) would be preserved.
So you could write a CSV like
Name,Age,City
Dominic,29,London
Bill,20,Seattle
This generates a DataTable with the column headings you have specified. Otherwise, stick to using the ordinals as you had before.
To integrate this, add a constructor (or extension method which I will get to shortly) that when passed a DataRow will strip out the data:
public UserData(DataRow row)
{
// At this point, the row may be reliable enough for you to
// attempt to reference by column names. If not, fall back to indexes
this.Name = Convert.ToString(row.Table.Columns.Contains("Name") ? row["Name"] : row[0]);
this.Age = Convert.ToInt32(row.Table.Columns.Contains("Age") ? row["Age"] : row[1]);
this.City = Convert.ToString(row.Table.Columns.Contains("City") ? row["City"] : row[2] );
}
Some would argue that the conversion process is not really the responsibility of the UserData class - since it is a POCO. Instead implement either an extension method in a ConverterExtensions.cs class.
public static class ConverterExtensions
{
public static void LoadFromDataRow<UserData>(UserData data, DataRow row)
{
data.Name = Convert.ToString(row.Table.Columns.Contains("Name") ? row["Name"] : row[0]);
data.Age = Convert.ToInt32(row.Table.Columns.Contains("Age") ? row["Age"] : row[1]);
data.City = Convert.ToString(row.Table.Columns.Contains("City") ? row["City"] : row[2] );
}
}
A more architectually sound method is to implement an interface that defines the conversion. Implement that interface with the conversion process and then store that interface reference internally. This will do the conversion for you, keeping the mapping completely seperate and keep your POCO nice and tidy. It will also allow you to "plug-in" mappers.
public interface ILoadFromDataRow<T>
{
void LoadFromDataRow<T>(T object, DataRow dr);
}
public class UserLoadFromDataRow : ILoadFromDataRow<UserData>
{
public void LoadFromDataRow<UserData>(UserData data, DataRow dr)
{
data.Name = Convert.ToString(row.Table.Columns.Contains("Name") ? row["Name"] : row[0]);
data.Age = Convert.ToInt32(row.Table.Columns.Contains("Age") ? row["Age"] : row[1]);
data.City = Convert.ToString(row.Table.Columns.Contains("City") ? row["City"] : row[2] );
}
}
public class UserData
{
private ILoadFromDataRow<UserData> converter;
public UserData(DataRow dr = null, ILoadFromDataRow<UserData> converter = new LoadFromDataRow<UserData>())
{
this.converter = (converter == null ? new LoadFromDataRow<UserData>() : converter);
if(dr!=null)
this.converter.LoadFromDataRow(this,dr);
}
// POCO as before
}
For your scenario, go for the extension methods. This interface method (called segregation) was the way to implement it before extension methods came about.

C# foreach over properties of objects contained within a Dictionary<String, ObjectType>

Is it possible to itterate directly over properties of the objects stored within a Dictionary collection in C#?
For example, I have a Dictionary called Fields of type Dictionary<String, Field>. The Field object has a property of Data which is of type XmlDataDocument so I would like to do something like,
foreach(XmlDataDocument fieldData in Fields.Values.Data){
}
I know it's pretty trivial since all I would need to do when itterating over Field objects instead would be,
XmlDataDocument fieldData = field.Data;
within the Field itteration however if there is a quicker way to do it I'd like to know :-)
In C# 3.0:
foreach (var data in Fields.Values.Select(x => x.Data))
{
}
It's not any "quicker" though.
yes, create a custom collection based on Dictionary and then add your own iterator to it...
public class MyFieldCollection: Dictionary<string, Field>
{
public IEnumerable<XmlDataDocument> Data
{
foreach(Field f in this.Values)
yield return f.Data;
}
}
Then in client code all you need to do is
MyFieldCollection MFC = new MyFieldCollection();
foreach (XmlDataDocument doc in MFC.Data)
{
// DoWhatever (doc );
}

Categories