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.
Related
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.
I'm learning the power of generics in C# in conjunction with NHibernate. I'd like to attempt the following in the pasted code.
In an attempt to do some post processing of N number of NHibernate objects I worked on a utility method leveraging generics to make it applicable to all NHibernate mapping classes we use now, or in the future. It works but I need to hard code each call for each mapping class. This is a pain and will need continuing updating as our schema and mappings change over time.
I do have an ever up-to-date list of all mapping classes by string name through the NHibernate mappings I generate on the fly. If there was a way to use this list of string names to call my generics based method, I'd be super happy.
Can anyone tell me if this is possible? Do I need to find another route?
Thanks so much in advance!!!
public static void ProcessSomeItems()
{
// *************************************************************
// As of now I have to list all classes as such to be processed
// It works but I have to update manually when new mapping classes are created
// *************************************************************
NHibDoSomethingUtil<AspnetMembership>();
NHibDoSomethingUtil<AspnetProfile>();
NHibDoSomethingUtil<AspnetRole>();
NHibDoSomethingUtil<AspnetUser>();
// and so forth...
// I have a up-to-date list of all mappings from "HbmMapping" and can get a list of all in the
// list form as below
List<string> mappingNames = new List<string>();
foreach (string mappingName in mappingNames)
{
Type theType = Type.GetType(mappingName);
// I know I'm getting Types and Generics classes and so forth all jumbled but
// how in the heck would I do something like the below?
NHibDoSomethingUtil<theType>(); // Obviously doesn't compile ;-)
}
}
// Generic method
public static void NHibDoSomethingUtil<T>() where T : class
{
using (ISession session = sourceDBSessionFactory.OpenSession())
{
foreach (dynamic item in new List<T>(session.QueryOver<T>().List()))
{
// Process item;
}
}
}
ecsousa gave great input and I was able to accomplish what I needed with something like the following.
foreach (HbmClass mappingClass in mapping.Items)
{
Console.WriteLine(" -- Discovered Mapping: " + mappingClass.Name);
Type mappingClassType = Type.GetType(mappingClass.Name);
var genericMethod = typeof(Migration).GetMethod("NHibDoSomethingUtil");
var method = genericMethod.MakeGenericMethod(mappingClassType);
method.Invoke(null, null);
}
You will need to use Reflection in order to accomplish this. Instead of directly calling NHibDoSomethingUtil, try this:
var genericMethod = typeof(TheClassName).GetMethod("NHibDoSomethingUtil");
var method = genericMethod.MakeGenericMethod(theType);
method.Invoke(null, null);
Note that you have to replace TheClassName by the class containing both methods.
Keep in mind the this kind of code is slow, and you should use it very carefully.
I'm new to ArrayList. As far as I know ArrayLists each element(?) can contain multiple values (excuse me for not using the correct terminology).
Now, here's the problem, If I make an ArrayList where each element contains only one value, I can easily print it, however as in the example below, if I attempt to make an ArrayList where each element contains multiple values - I cannot print the values of each element in the ArrayList.
If I try to use foreach or for loop using Console.Writeline(list[i]); all I get is the namespace.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication13
{
class Register
{
public string name;
public string surname;
public void RegisterData()
{
Console.Write("Enter your name: "); name = Console.ReadLine();
Console.Write("Enter your surname: "); surname= Console.ReadLine();
}
public void PrintData()
{
Console.WriteLine("Name: " + name + " Surname: " + surname);
}
}
class Program
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
Register temp = new Register();
temp.RegisterData();
list.Add(temp);
for (int i = 0; i < list.Count; i++)
{
// How can I use the PrintData (without modifying it) method to print the ArrayList values?
}
}
}
}
How could I be able to print the values of the ArrayList without using the PrintData Method?
Thank you in advance!
ArrayList is .NET 1.1 stuff. You should use List instead. It's generic and superior over ArrayList.
So you define your list like
List<Register> list = new List<Register>();
...
list.Add(temp);
And in the foreach loop, you can access the properties of your Register class. You can do that with ArrayList, too. But then you need casts. Why not use List then... Looks much cleaner:
foreach (Register register in list)
{
Console.WriteLine("Name: " + register.name + " Surname: " + register.surname);
// or
// register.PrintData();
}
You're adding an instance of Register to your ArrayList. When you index into it, list[i], that returns this instance.
When you attempt to print this instance, you are in fact calling this method: Console.WriteLine(object), as it is the best fit. This method needs to get a string from your object, so it calls ToString() on it.
The default implementation of ToString() returns the namespace and type name for the current object. In this case, "WhateverYourBloodyNamespaceIs.Register".
Now you know you have an object of type Register, you can get name and surname from it, format them properly and write those to the console. You've got a PrintData method, use it.
ArrayList isn't generic, so you must cast from object back to Register. Or, be a big boy and use List<Register> instead of that 2002 ArrayList crap.
Whoever told you to use an ArrayList is playing tricks on you.
You need to pick up a copy of CLR Via C#. Skip the first two chapters. Start reading. Go. Run. I'm not joking.
Override ToString() method in your Register class, then you pass the 'temp' variable to Console.WriteLine() method.
I'm looking for some advices how to deal with similar example problem:
string A;
string B;
string C;
string D;
...
void Initialize()
{
A = someObject.A.ToString();
B = someObject.B.ToString();
C = someObject.C.ToString("F");
D = someObject.D.ToString();
...
}
My class has a lot of numeric variables and I need to pass their values to these strings easily for large quantity of such variables. Let's say it is RPG stats based game and a lot of stats, factors etc. play here a role. Managing such a many variables by copy pasting is a pain.
Question: how can I simplify work with lots of variables.
I would like to know some solutions for this problem since I don't have any experience with it and I don't know anyone or any source to search for a solutions.
I personally thought about using some kind of structure, dicitonary or database.
EDIT: To be precise I've created a class called Character and it has a lot of properties like xp, life, gold etc. Now I want to do a GUI using class called Label that draw text on the screen, so I'm coding GUI class containing Label objects that on various events will react accordingly. For example I need to assign at the beginning to my labels specified text like:
Label life, gold, xp, etc;
life.text = Character.instance.Life.ToString();
gold.text = Character.instance.Gold.ToString();
xp.text = Character.instance.XP.ToString();
...
You should include more details in this question, what you exactly want to achieve.
From the information you provided, I assume you want to read the properties of someObject dynamically, execute ToString() on them and assign its value to some variables. That is a typical reflection assignment. You might want to read a chapter covering reflection on MSDN
If the assumption above is correct, you could do the following:
Dictionary<string, string> propertyValues = new Dictionary<string, string>();
// We access the type information via reflection. What properties your someObject has?
PropertyInfo[] propertyInfos = someObject.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var propertyInfo in propertyInfos)
{
// we get the value of the property, described by propertyInfo, from our someObject
object value = propertyInfo.GetValue(someObject, null);
if (value != null)
{
// do your special type handling here and execute ToString()
if(value.GetType() == typeof(int))
propertyValues[propertyInfo.Name] = ((int)value).ToString("F");
if(value.GetType() == ...)
else
propertyValues[propertyInfo.Name] = value.ToString();
}
}
// so if your someObject had a property A you can access it as follows:
string A = propertyValues["A"];
// or you loop the dictionary and do some suff with it (which makes much more sense):
foreach(var keyValuePair in propertyValues)
Console.WriteLine("Property {0} = {1}", keyValuePair.Key, keyValuePair.Value);
It's a little hard for us to say without seeing your exact design/requirements, but a simple way to do this which handles many situations is to store the object itself, rather than copying its properties or fields.
For example, in your version you might have:
private int A;
private string B;
private double C;
public void Initialize(DataObject data)
{
A = data.A;
B = data.B;
C = data.C;
}
public void DoSomething() //Just an arbitrary method using the data
{
return B + " " + A.ToString();
}
Whereas, you could more simply do:
private DataObject _data;
public void Initialize(DataObject data)
{
_data = data;
}
public void DoSomething() //Just an arbitrary method using the data
{
return _data.B + " " + _data.A.ToString();
}
Depending on how the values get into 'someObject', a first step into having less code would be to define the properties to be String, doing the conversion from int to String in the get accessor method. Even though this just moves the ToString() to a different location, it will reduce the code each time you go to get the value.
I'm an experienced PHP developer transitioning to C#. At present I am working on a Windows Forms application.
I found in my searches that C# doesn't support associative arrays in the same loose fashion PHP does. I have found info on Dictionary and something about "structs" which seem to be class objects.
The trouble I am having is getting my head around not only an Associative array, but a multi dimensional one that I want to use for keeping multiple counts in a series of loops.
The application is reading a text log file, searching for a predefined string, pulling out the date on that line when the string is found, and incrementing a count for that string match on that date.
In PHP, it would be as easy as this:
// Initialize
$count_array[$string_date][$string_keyword] = 0;
...
// if string is found
$count_array[$string_date][$string_keyword] += 1;
...
// To ouput contents of array
foreach($count_array as $date -> $keyword_count_array) {
echo $date; // output date
foreach($keyword_count_array as $keyword -> $count) {
echo $keyword . ": " . $count;
}
}
It seems to be a little more involved in C# (which isn't a bad thing). I have tried using an suggestion I found on another similar question but I don't really follow how to either increment or iterate/output the contents:
// Initialize
var count_array = new Dictionary<string, Dictionary<string, int>>();
count_array = null;
...
// if string is found - I think the second reference is supposed to be a Dictionary object??
count_array[string_date.ToShortDateString()][string_keyword]++;
...
// To ouput contents of "array"
foreach (KeyValuePair<string, Dictionary<string, int>> kvp in exportArray)
{
foreach(KeyValuePair<string, int> kvp2 in kvp.Value)
{
MessageBox.Show(kvp.Key + " - " + kvp2.Key + " = " + kvp2.Value);
}
}
Am I even on the right track? Or does someone have a better/cleaner method of mimicing the PHP code above?
UPDATE
With the above C# code, I actually get an error at the "// if string is found " line. The error is "Object reference is not set to an instance of an object". I am assuming that it is because I have a string in the secound reference, not a Dictionary object. So right now, I am unsure how to increment.
UPDATE 2
Thanks everyone for your time. Current code is now functional thanks to understanding how Dictionary's work. However all advice regarding the use of classes and objects for this situation is not lost either. I may refactor to suit.
The code itself looks sound, the only thing I see missing is there are no checks to see if the values exist before incrementing them.
Before you call
count_array[string_date.ToShortDateString()][string_keyword]++;
You'll need to do:
string shortDate = string_date.ToShortDateString();
if (!count_array.ContainsKey(shortDate))
{
count_array.Add(shortDate, new Dictionary<string, int>());
}
if (!count_array[shortDate].ContainsKey(string_keyword))
{
count_array[shortDate].Add(string_keyword, 0);
}
Before you try incrementing anything.
You need to initialize your dictionary entries by calling .Add or ["key"] = value. Calling ++ on an uninitialized dictionary entry won't work. Depending on what exactly it is you're trying to accomplish though it might be a good idea to use a class.
You can use a tuple to create a multi-dimensional key for use in a Dictionary.
Dictionary<Tuple<TKey1,TKey2>,TValue>
Or a Dictionary of Dictionary:
Dictionary<TKey1,Dictionart<TKey2,Tvalue>>
The second one is more annoying to work with, but has the upside that you can index into it with just the first key and then get all key-value pairs associated with that key.
But perhaps you can use some linq, but your code is a bit incomplete for that.
What about creating a class for this?
public class LogEntry
{
private List<int> _lines = new List<int>();
public string LogContent { get;set; }
public DateTime Time { get;set; }
public List<int> Lines { get { return _lines; } }
}
You'd still have a dictionary of probably DateTime, LogEntry? Not entirely sure what exactly you need / what the key is.
Anyways, creating a class seems to be the "correct" way as you can express your intend more clearly.
Your approach can work, however, you need to understand Dictionary is a reference type, which means it has to be created prior to use. You create the “top-level” Dictionary, but the “second-level” dictionaries need to be created as well. But in
count_array[string_date.ToShortDateString()][string_keyword]++;
you count on count_array[string_date.ToShortDateString()] being already created (so that it can be queried). And another problem is that Dictionary<Key, Value> behavior is that an attempt to access an item which does not exist results in an exception (KeyNotFoundException). There is a more lenient TryGetValue method for you to use. Combined, you need to do something along the lines of:
// Initialize
var count_array = new Dictionary<string, Dictionary<string, int>>();
// if string is found - I think the second reference is supposed to be a Dictionary object??
Dictionary<string, int> perDateDict;
var dateKey = string_date.ToShortDateString();
if (!count_array.TryGetValue(dateKey, out perDateDict)
{
perDateDict = new Dictionary<string, int>();
count_array.Add(adteKey, perDateDict);
}
int prevValue;
// note that when not found, prevValue will be zero, which is what we need
perDateDict.TryGetValue(string_keyword, out prevValue);
perDateDict[string_keyword] = prevValue+1;
// To ouput contents of "array"
foreach (KeyValuePair<string, Dictionary<string, int>> kvp in exportArray)
{
foreach(KeyValuePair<string, int> kvp2 in kvp.Value)
{
MessageBox.Show(kvp.Key + " - " + kvp2.Key + " = " + kvp2.Value);
}
}
One thing you must be make sure Dictionary is not a ValueType and is not auto initialized.
Hence when you say count_array = null it means you are resetting the reference to null location. Just remove the line.
Your code should look like :
var count_array = new Dictionary<string, Dictionary<string, int>>();
// if string is found - I think the second reference is supposed to be a Dictionary object??
string dt = DateTime.Now.ToShortDateString();
count_array[dt] = new Dictionary<string, int>(); //It is important as you should always give appropriate refernece before doing a fetch.
count_array[dt]["key"] = 0; //Value types are defaults to 0 so it is not explicitely required.
//Now you can do
count_array[dt]["key"]++;
// To ouput contents of "array"
foreach (KeyValuePair<string, Dictionary<string, int>> kvp in count_array)
{
foreach (KeyValuePair<string, int> kvp2 in kvp.Value)
{
Console.WriteLine(kvp.Key + " - " + kvp2.Key + " = " + kvp2.Value);
}
}
You can also use ?? operator to ensure that when Dictionary is null, you assign a new reference.
count_array[dt] = count_array[dt] ?? new Dictionary();
I hope this will help you even you should recode this properly.
You need to start thinking in OO terms. In production code I would give the classes some destination to print to, instead of going directly to Console, and maybe use strategy or similar to format the text, but essentially this is the OO way of thinking about the problem.
class Log {
Dictionary<DateTime, List<LogEntry>} Entries { get; private set; }
public void PrintLogs()
{
foreach (var date in Entries.Keys)
{
Console.WriteLine(date);
foreach (var entry in Entries[date])
{
entry.PrintEntry();
}
}
}
}
class LogEntry {
public List<string> EntryLines { get; set; }
public DateTime Date { get; set; }
public void PrintEntry()
{
foreach (var line in EntryLines)
Console.WriteLine(line);
}
}