C# Newtonsoft Json deserialize array of different inheirted objects from json file - c#

I have an array of objects which I want to write to a json file and recover it later
Point - Visual Basic class:
<Serializable> Public Class Point
Implements IComparable
Public Property X As Integer
Public Property Y As Integer
Public Sub New()
X = 0
Y = 0
End Sub
Public Sub New(ByVal x As Integer, ByVal y As Integer)
Me.X = x
Me.Y = y
End Sub
End Class
Point3D - C# class
[Serializable]
public class Point3D : Point
{
public int Z { get; set; }
public Point3D() : base()
{
var rnd = new Random();
Z = rnd.Next(10);
}
public Point3D(int x, int y, int z) : base(x, y)
{
this.Z = z;
}
}
Serialization works fine but when I try to deserialize json from file, all objects presented as Point3D (even the ones with only X and Y variables).
Before deserialization:
enter image description here
After:
enter image description here
Serialization code block:
using (var fs = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write))
{
switch (Path.GetExtension(dlg.FileName))
{
...
case ".json":
var jf = new JsonSerializer();
jf.TypeNameHandling= TypeNameHandling.Auto;
using (var w = new StreamWriter(fs))
jf.Serialize(w, points);
break;
}
}
Deserialization code block:
using (var fs = new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read))
{
switch (Path.GetExtension(dlg.FileName))
{
...
case ".json":
var jf = new JsonSerializer();
using (var r = new StreamReader(fs))
points = (Point[])jf.Deserialize(r, typeof(Point3D[]));
break;
}
}
Code I use to initialize array of objects (Point and Point3D):
private void buttonCreate_Click(object sender, EventArgs e)
{
points = new Point[5];
var rnd = new Random();
for (int i = 0; i < points.Length; i++)
{
points[i] = rnd.Next(3) % 2 == 0 ? new Point() : new Point3D();
}
listBox.DataSource = points;
}
And json string
{"$type":"PointLib.Point[], PointLib","$values":[{"$type":"LabOneFormsApp.Point3D, LabOneFormsApp","Z":6,"X":0,"Y":0},{"$type":"LabOneFormsApp.Point3D, LabOneFormsApp","Z":1,"X":0,"Y":0},{"$type":"PointLib.Point, PointLib","X":0,"Y":0},{"$type":"PointLib.Point, PointLib","X":0,"Y":0},{"$type":"PointLib.Point, PointLib","X":0,"Y":0}]}
I tried adding
jf.TypeNameHandling= TypeNameHandling.Auto;
to my code but it does not seem to work for me. Any ideas?

I've decided to do it simple way
case ".json":
var reader = new StreamReader(fs).ReadToEnd();
var jsonFile = JsonConvert.DeserializeObject<List<Dictionary<string, int>>>(reader);
if (jsonFile != null)
{
points = new Point[jsonFile.Count];
for (int i = 0; i < jsonFile.Count; i++)
{
var pointDict = jsonFile[i];
switch (pointDict.Count)
{
case 2:
points[i] = new Point(pointDict["X"], pointDict["Y"]);
break;
case 3:
points[i] = new Point3D(pointDict["X"], pointDict["Y"], pointDict["Z"]);
break;
}
}
}
else
{
points = null;
}
Thanks everyone for the answers!

Related

Random.Next - Am I silently creating new instances?

Random.Next() randomness failures are almost always caused by creating and then using multiple instances of System.Random with the same seed, either with a time seed or a manual one. However, this is the only instance creation code in my class:
System.Random rNG;
if (string.IsNullOrEmpty(Map.Seed))
{
rNG = new System.Random();
}
else
{
rNG = new System.Random(Map.Seed.GetHashCode());
}
Looping through this second attempt code correctly creates random numbers:
var resourceRoll = rNG.Next(0, this.ResourceByRoll.Count);
var resourceRow = from row in this.ProcGenResourceTable.AsEnumerable()
.Where(row => row["Resource"].Equals(
this.ResourceByRoll[resourceRoll]
))
Looping through this original attempt code often creates the same number twice in a row:
var resourceRow = from row in this.ProcGenResourceTable.AsEnumerable()
.Where(row => row["Resource"].Equals(
this.ResourceByRoll[rNG.Next(0, this.ResourceByRoll.Count)]
))
Am I somehow silently creating a new instance of System.Random when using a Random.Next call as a dictionary index? Why does my original code often return the same number twice in a row?
If it matters:
This class is a Unity script
I am using System.Random, not UnityEngine.Random
My complete class is below:
using Assets.Code.Tools;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using UnityEngine;
public class Map : MonoBehaviour
{
public static int Length { get; set; }
public static int Width { get; set; }
public static int ResourceChanceDenominator { get; set; }
public static string Seed { get; set; }
private static int[,] objectGrid;
private DataTable ProcGenResourceTable { get; set; }
private Dictionary<int, string> ResourceByRoll { get; set; }
private List<GameObject> prefabTrees;
private List<GameObject> prefabStones;
private void Start()
{
this.prefabTrees = GeneralTools.GetPrefabsWithTag("Tree");
this.prefabStones = GeneralTools.GetPrefabsWithTag("Stone");
GenerateMap();
}
public void GenerateMap()
{
var procGenResourceTable = Resources.Load("ProcGenResourceTable") as TextAsset;
if (procGenResourceTable != null)
{
this.ProcGenResourceTable = GeneralTools.GetDataTableFromCSV(procGenResourceTable, "|", true, false);
}
else
{
Console.WriteLine("ProcGenResourceTable could not be found");
return;
}
Map.objectGrid = new int[Map.Width, Map.Length];
this.ResourceByRoll = GetPopulatedResourceByRollDictionary();
System.Random rNG;
if (string.IsNullOrEmpty(Map.Seed))
{
rNG = new System.Random();
}
else
{
rNG = new System.Random(Map.Seed.GetHashCode());
}
for (var i = 0; i < Map.Length; i++)
{
for (var j = 0; j < Map.Width; j++)
{
var roll = rNG.Next(Map.ResourceChanceDenominator);
if (roll == 1)
{
// var resourceRoll = rNG.Next(0, this.ResourceByRoll.Count);
var resourceRow = from row in this.ProcGenResourceTable.AsEnumerable()
.Where(row => row["Resource"].Equals(
this.ResourceByRoll[rNG.Next(0, this.ResourceByRoll.Count)]
))
select new
{
ModelFamily = row["Model Family"],
Tags = row["Tags"]
};
foreach (var row in resourceRow)
{
GameObject resource = null;
switch (row.ModelFamily)
{
case "Tree":
resource = Instantiate(this.prefabTrees[rNG.Next(this.prefabTrees.Count - 1)], new Vector3(i, 0, j), new Quaternion());
break;
case "Stone":
resource = Instantiate(this.prefabStones[rNG.Next(this.prefabStones.Count - 1)], new Vector3(i, 0, j), new Quaternion());
break;
default:
resource = Instantiate(this.prefabTrees[rNG.Next(this.prefabTrees.Count - 1)], new Vector3(i, 0, j), new Quaternion());
break;
}
var tagsListForResource = row.Tags.ToString().Split(new char[] { '|' }).ToList();
if (tagsListForResource.Contains("Resource"))
{
resource.tag = "Resource";
}
}
}
}
}
}
private Dictionary<int, string> GetPopulatedResourceByRollDictionary()
{
var resourceByRoll = new Dictionary<int, string>();
foreach (DataRow row in this.ProcGenResourceTable.Rows)
{
if (!string.IsNullOrEmpty(row["Weight"].ToString()))
{
for (var i = 0; i < Convert.ToInt32(row["Weight"]); i++)
{
resourceByRoll.Add(resourceByRoll.Count, row["Resource"].ToString());
}
}
}
return resourceByRoll;
}
}
I misunderstood what was going on - this is not related to repeated random numbers.
The answer to Michael Welch's question is that I am seeing trees and rocks generate on the same coordinates.
I expected this code to only generate one row of results:
var resourceRow = from row in this.ProcGenResourceTable.AsEnumerable()
.Where(row => row["Resource"].Equals(
this.ResourceByRoll[rNG.Next(0, this.ResourceByRoll.Count)]
))
However, it is sometimes returns more, because .Where() does some looping of its own that re-rolls the number each time.
Having multiple rows returned causes the foreach block to generate multiple prefabs at the coordinate instead of just one.

Check if an stl file may contain two models

An stl file may contain 2 3D models. Is there any way I can detect if there are 2 or more models stored in one stl file?
In my current code, it can detect that there are 2 models in the example, but there are instances that it detects a lot of model even though it only has one.
The Triangle class structure has Vertices that contains 3 points (x, y, z)..
Sample STL File:
EDIT: Using #Gebb's answer this is how I implemented it:
private int GetNumberOfModels(List<TopoVertex> vertices)
{
Vertex[][] triangles = new Vertex[vertices.Count() / 3][];
int vertIdx = 0;
for(int i = 0; i < vertices.Count() / 3; i++)
{
Vertex v1 = new Vertex(vertices[vertIdx].pos.x, vertices[vertIdx].pos.y, vertices[vertIdx].pos.z);
Vertex v2 = new Vertex(vertices[vertIdx + 1].pos.x, vertices[vertIdx + 1].pos.y, vertices[vertIdx + 1].pos.z);
Vertex v3 = new Vertex(vertices[vertIdx + 2].pos.x, vertices[vertIdx + 2].pos.y, vertices[vertIdx + 2].pos.z);
triangles[i] = new Vertex[] { v1, v2, v3 };
vertIdx += 3;
}
var uniqueVertices = new HashSet<Vertex>(triangles.SelectMany(t => t));
int vertexCount = uniqueVertices.Count;
// The DisjointUnionSets class works with integers, so we need a map from vertex
// to integer (its id).
Dictionary<Vertex, int> indexedVertices = uniqueVertices
.Zip(
Enumerable.Range(0, vertexCount),
(v, i) => new { v, i })
.ToDictionary(vi => vi.v, vi => vi.i);
int[][] indexedTriangles =
triangles
.Select(t => t.Select(v => indexedVertices[v]).ToArray())
.ToArray();
var du = new XYZ.view.wpf.DisjointUnionSets(vertexCount);
// Iterate over the "triangles" consisting of vertex ids.
foreach (int[] triangle in indexedTriangles)
{
int vertex0 = triangle[0];
// Mark 0-th vertexes connected component as connected to those of all other vertices.
foreach (int v in triangle.Skip(1))
{
du.Union(vertex0, v);
}
}
var connectedComponents =
new HashSet<int>(Enumerable.Range(0, vertexCount).Select(x => du.Find(x)));
return connectedComponents.Count;
}
In some cases, it produces the correct output, but for the example image above, it outputs 3 instead of 2. I am now trying to optimize the snippet #Gebb gave to use float values since I believe that the floating points are necessary to the comparisons. Does anyone have a way to do that as well? Maybe I need another perspective.
You could do this by representing vertices and connections between them as a graph and finding the number of connected components of the graph with the help of the Disjoint-set data structure.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Vertex = System.ValueTuple<double,double,double>;
namespace UnionFindSample
{
internal class DisjointUnionSets
{
private readonly int _n;
private readonly int[] _rank;
private readonly int[] _parent;
public DisjointUnionSets(int n)
{
_rank = new int[n];
_parent = new int[n];
_n = n;
MakeSet();
}
// Creates n sets with single item in each
public void MakeSet()
{
for (var i = 0; i < _n; i++)
// Initially, all elements are in
// their own set.
_parent[i] = i;
}
// Finds the representative of the set
// that x is an element of.
public int Find(int x)
{
if (_parent[x] != x)
{
// if x is not the parent of itself, then x is not the representative of
// his set.
// We do the path compression by moving x’s node directly under the representative
// of this set.
_parent[x] = Find(_parent[x]);
}
return _parent[x];
}
// Unites the set that includes x and
// the set that includes x
public void Union(int x, int y)
{
// Find representatives of two sets.
int xRoot = Find(x), yRoot = Find(y);
// Elements are in the same set, no need to unite anything.
if (xRoot == yRoot)
{
return;
}
if (_rank[xRoot] < _rank[yRoot])
{
// Then move x under y so that depth of tree remains equal to _rank[yRoot].
_parent[xRoot] = yRoot;
}
else if (_rank[yRoot] < _rank[xRoot])
{
// Then move y under x so that depth of tree remains equal to _rank[xRoot].
_parent[yRoot] = xRoot;
}
else
{
// if ranks are the same
// then move y under x (doesn't matter which one goes where).
_parent[yRoot] = xRoot;
// And increment the result tree's
// rank by 1
_rank[xRoot] = _rank[xRoot] + 1;
}
}
}
internal class Program
{
private static void Main(string[] args)
{
string file = args[0];
Vertex[][] triangles = ParseStl(file);
var uniqueVertices = new HashSet<Vertex>(triangles.SelectMany(t => t));
int vertexCount = uniqueVertices.Count;
// The DisjointUnionSets class works with integers, so we need a map from vertex
// to integer (its id).
Dictionary<Vertex, int> indexedVertices = uniqueVertices
.Zip(
Enumerable.Range(0, vertexCount),
(v, i) => new {v, i})
.ToDictionary(vi => vi.v, vi => vi.i);
int[][] indexedTriangles =
triangles
.Select(t => t.Select(v => indexedVertices[v]).ToArray())
.ToArray();
var du = new DisjointUnionSets(vertexCount);
// Iterate over the "triangles" consisting of vertex ids.
foreach (int[] triangle in indexedTriangles)
{
int vertex0 = triangle[0];
// Mark 0-th vertexes connected component as connected to those of all other vertices.
foreach (int v in triangle.Skip(1))
{
du.Union(vertex0, v);
}
}
var connectedComponents =
new HashSet<int>(Enumerable.Range(0, vertexCount).Select(x => du.Find(x)));
int count = connectedComponents.Count;
Console.WriteLine($"Number of connected components: {count}.");
var groups = triangles.GroupBy(t => du.Find(indexedVertices[t[0]]));
foreach (IGrouping<int, Vertex[]> g in groups)
{
Console.WriteLine($"Group id={g.Key}:");
foreach (Vertex[] triangle in g)
{
string tr = string.Join(' ', triangle);
Console.WriteLine($"\t{tr}");
}
}
}
private static Regex _triangleStart = new Regex(#"^\s+outer loop");
private static Regex _triangleEnd = new Regex(#"^\s+endloop");
private static Regex _vertex = new Regex(#"^\s+vertex\s+(\S+)\s+(\S+)\s+(\S+)");
private static Vertex[][] ParseStl(string file)
{
double ParseCoordinate(GroupCollection gs, int i) =>
double.Parse(gs[i].Captures[0].Value, CultureInfo.InvariantCulture);
var triangles = new List<Vertex[]>();
bool isInsideTriangle = false;
List<Vertex> triangle = new List<Vertex>();
foreach (string line in File.ReadAllLines(file))
{
if (isInsideTriangle)
{
if (_triangleEnd.IsMatch(line))
{
isInsideTriangle = false;
triangles.Add(triangle.ToArray());
triangle = new List<Vertex>();
continue;
}
Match vMatch = _vertex.Match(line);
if (vMatch.Success)
{
double x1 = ParseCoordinate(vMatch.Groups, 1);
double x2 = ParseCoordinate(vMatch.Groups, 2);
double x3 = ParseCoordinate(vMatch.Groups, 3);
triangle.Add((x1, x2, x3));
}
}
else
{
if (_triangleStart.IsMatch(line))
{
isInsideTriangle = true;
}
}
}
return triangles.ToArray();
}
}
}
I'm also using the fact that System.ValueTuple implements Equals and GetHashCode in an appropriate way, so we can easily compare vertices (this is used implicitly by HashSet) and use them as keys in a dictionary.

How to read 2D int array from binary file in c#?

I have a 2D integer array to store x,y coordinates. I checked out a few functions to write 2D array into a file but cannot find anything that is able to read that binary file on load and push it into a new 2 dimensional integer array.
This is my world generator function which saves it to the file:
public WorldGenerator()
{
int worldSizeX = 100;
int worldSizeY = 100;
int[,] world = new int[worldSizeX*worldSizeY, 2];
Logger.log("Generating world...");
for(int x = 0; x < worldSizeX; x++)
{
for(int y = 0; y < 2; y++)
{
System.Random random = new System.Random();
int itemID = random.Next(0, 1);
world[x, y] = itemID;
}
}
FileStream fs = new FileStream(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/ConsoleGame/world/default.wd", FileMode.OpenOrCreate, FileAccess.Write);
BinaryWriter bw = new BinaryWriter(fs);
for (int x = 0; x < worldSizeX; x++)
{
for (int y = 0; y < 2; y++)
{
bw.Write(world[x, y]);
}
}
bw.Close();
fs.Close();
Logger.log("World generated.");
}
Any good idea that could work for reading this file in? I should get back a 2D integer array and world[0,0] should get me the itemid. I am new to c# and this would be just a basic console application.
I have also seen others answering similar questions but none of them are worked for me yet. Maybe because this save function is wrong or something else.
EDIT:
Here is how I load the file:
using (var filestream = File.Open(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/ConsoleGame/world/default.wd", FileMode.Open))
using (var binaryStream = new BinaryReader(filestream))
{
while (binaryStream.PeekChar() != -1)
{
Console.WriteLine(binaryStream.ReadInt32());
}
}
need Newtonsoft.Json
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace ConsoleApp18
{
class Program
{
static void Main(string[] args)
{
int worldSizeX = 100;
int worldSizeY = 100;
int[,] world = new int[worldSizeX * worldSizeY, 2];
System.Random random = new System.Random();
for (int x = 0; x < worldSizeX; x++)
{
for (int y = 0; y < 2; y++)
{
int itemID = random.Next(0, 2);
world[x, y] = itemID;
}
}
string json = JsonConvert.SerializeObject(world, Formatting.Indented);
System.IO.File.WriteAllText("WriteText.txt", json);
string text = System.IO.File.ReadAllText("WriteText.txt");
int[,] deserialized = JsonConvert.DeserializeObject<int[,]>(text);
//use "deserialized"
}
}
}
What you need is called "Serialization".
Start with the simple builtin binary serializer.
Serializable attribute does the magic here.
By the moment you'll realize it is not the best option, you'll be able to use something more suiting your needs, like proto-buf.
I've also changed ints to shorts in your example. I doubt you need 32 bits for each world cell, so we can save a bit of hard drive space.
[Serializable]
public class WorldState
{
public short[,] Items { get; set; }
public void Save(string filename)
{
if (filename == null) throw new ArgumentNullException(nameof(filename));
using (var file = File.Create(filename))
{
var serializer = new BinaryFormatter();
serializer.Serialize(file, this);
}
}
public static WorldState Load(string filename)
{
if (filename == null) throw new ArgumentNullException(nameof(filename));
if (!File.Exists(filename)) throw new FileNotFoundException("File not found", filename);
using (var file = File.OpenRead(filename))
{
var serializer = new BinaryFormatter();
return serializer.Deserialize(file) as WorldState;
}
}
}
public class WorldStateTests
{
[Fact]
public void CanSaveAndLoad()
{
var ws = new WorldState
{
Items = new short[,]
{
{ 1, 2, 3, 4 },
{ 1, 2, 3, 4 },
{ 1, 2, 3, 4 },
{ 1, 2, 3, 4 }
}
};
// save the world state to file. Find it and see what's inside
ws.Save("./ws.bin");
// load the world back
var loaded = WorldState.Load("./ws.bin");
// check a new world state got loaded
Assert.NotNull(loaded);
// and it still has items
Assert.NotEmpty(loaded.Items);
// and the items are the same as we saved
Assert.Equal(ws.Items, loaded.Items);
}
}

Implementing a fast DBSCAN in C#

I tried to implement a DBSCAN in C# using kd-trees. I followed the implementation from:
http://www.yzuzun.com/2015/07/dbscan-clustering-algorithm-and-c-implementation/
public class DBSCANAlgorithm
{
private readonly Func<PointD, PointD, double> _metricFunc;
public DBSCANAlgorithm(Func<PointD, PointD, double> metricFunc)
{
_metricFunc = metricFunc;
}
public void ComputeClusterDbscan(ScanPoint[] allPoints, double epsilon, int minPts, out HashSet<ScanPoint[]> clusters)
{
clusters = null;
var allPointsDbscan = allPoints.Select(x => new DbscanPoint(x)).ToArray();
var tree = new KDTree.KDTree<DbscanPoint>(2);
for (var i = 0; i < allPointsDbscan.Length; ++i)
{
tree.AddPoint(new double[] { allPointsDbscan[i].ClusterPoint.point.X, allPointsDbscan[i].ClusterPoint.point.Y }, allPointsDbscan[i]);
}
var C = 0;
for (int i = 0; i < allPointsDbscan.Length; i++)
{
var p = allPointsDbscan[i];
if (p.IsVisited)
continue;
p.IsVisited = true;
DbscanPoint[] neighborPts = null;
RegionQuery(tree, p.ClusterPoint.point, epsilon, out neighborPts);
if (neighborPts.Length < minPts)
p.ClusterId = (int)ClusterIds.NOISE;
else
{
C++;
ExpandCluster(tree, p, neighborPts, C, epsilon, minPts);
}
}
clusters = new HashSet<ScanPoint[]>(
allPointsDbscan
.Where(x => x.ClusterId > 0)
.GroupBy(x => x.ClusterId)
.Select(x => x.Select(y => y.ClusterPoint).ToArray())
);
return;
}
private void ExpandCluster(KDTree.KDTree<DbscanPoint> tree, DbscanPoint p, DbscanPoint[] neighborPts, int c, double epsilon, int minPts)
{
p.ClusterId = c;
for (int i = 0; i < neighborPts.Length; i++)
{
var pn = neighborPts[i];
if (!pn.IsVisited)
{
pn.IsVisited = true;
DbscanPoint[] neighborPts2 = null;
RegionQuery(tree, pn.ClusterPoint.point, epsilon, out neighborPts2);
if (neighborPts2.Length >= minPts)
{
neighborPts = neighborPts.Union(neighborPts2).ToArray();
}
}
if (pn.ClusterId == (int)ClusterIds.UNCLASSIFIED)
pn.ClusterId = c;
}
}
private void RegionQuery(KDTree.KDTree<DbscanPoint> tree, PointD p, double epsilon, out DbscanPoint[] neighborPts)
{
int totalCount = 0;
var pIter = tree.NearestNeighbors(new double[] { p.X, p.Y }, 10, epsilon);
while (pIter.MoveNext())
{
totalCount++;
}
neighborPts = new DbscanPoint[totalCount];
int currCount = 0;
pIter.Reset();
while (pIter.MoveNext())
{
neighborPts[currCount] = pIter.Current;
currCount++;
}
return;
}
}
//Dbscan clustering identifiers
public enum ClusterIds
{
UNCLASSIFIED = 0,
NOISE = -1
}
//Point container for Dbscan clustering
public class DbscanPoint
{
public bool IsVisited;
public ScanPoint ClusterPoint;
public int ClusterId;
public DbscanPoint(ScanPoint point)
{
ClusterPoint = point;
IsVisited = false;
ClusterId = (int)ClusterIds.UNCLASSIFIED;
}
}
, and modifying the regionQuery(P, eps) to invoke the nearest neighbour function of a kd-tree. To do so, I used the kd-sharp library for C#, which is one of the fastest kd-tree implementations out there.
However, when given a dataset of about 20000 2d points, its performance is in the region of 40s, as compared to the scikit-learn python implementation of DBSCAN, which given the same parameters, takes about 2s.
Since this algorithm is for a C# program that I am writing, I am stuck using C#. As such, I would like to find out what am I still missing out in terms of optimization of the algorithm?

Write and Read an Array to a Binary File

I have an array consisting of 1 string value and 2 int values, which I would like to write to a binary file.
It consists of name, index and score.
I have attached the array code below, how could I write this to a file?
Player[] playerArr = new Player[10];
int index = 0;
index = index + 1; // when a new player is added the index is increased by one
Player p = new Player(txtName3.Text, index, Convert.ToInt16(txtScore.Text)); // set the values of the object p
p.refName = txtName3.Text; // set refName to be the string value that is entered in txtName
p.refTotalScore = Convert.ToInt16(txtScore.Text);
playerArr[index] = p; // set the p object to be equal to a position inside the array
I would also like to sort each instantiation of the array to be output in descending order of score. How could this be done?
The file handling code I have so far is:
private static void WriteToFile(Player[] playerArr, int size)
{
Stream sw;
BinaryFormatter bf = new BinaryFormatter();
try
{
sw = File.Open("Players.bin", FileMode.Create);
bf.Serialize(sw, playerArr[0]);
sw.Close();
sw = File.Open("Players.bin", FileMode.Append);
for (int x = 1; x < size; x++)
{
bf.Serialize(sw, playerArr[x]);
}
sw.Close();
}
catch (IOException e)
{
MessageBox.Show("" + e.Message);
}
}
private int ReadFromFile(Player[] playerArr)
{
int size = 0;
Stream sr;
try
{
sr = File.OpenRead("Players.bin");
BinaryFormatter bf = new BinaryFormatter();
try
{
while (sr.Position < sr.Length)
{
playerArr[size] = (Player)bf.Deserialize(sr);
size++;
}
sr.Close();
}
catch (SerializationException e)
{
sr.Close();
return size;
}
return size;
}
catch (IOException e)
{
MessageBox.Show("\n\n\tFile not found" + e.Message);
}
finally
{
lstLeaderboard2.Items.Add("");
}
return size;
}
For the first part, you need to mark your class as Serializable, like this:
[Serializable]
public class Player
It's fine to Append to a new file, so you can change your code to this:
sw = File.Open(#"C:\Players.bin", FileMode.Append);
for (int x = 0; x < size; x++)
{
bf.Serialize(sw, playerArr[x]);
}
sw.Close();
(with the appropriate exception handling, and you'll obviously need to amend this if the file might already exist).
For the second part, you can sort an array like this using LINQ:
var sortedList = playerArr.OrderBy(p => p.Score);
If you require an array as output, do this:
var sortedArray = playerArr.OrderBy(p => p.Score).ToArray();
(Here, Score is the name of the property on the Player class by which you want to sort.)
If you'd like any more help, you'll need to be more specific about the problem!

Categories

Resources