Games Of Reflections
i got this task from an interview, got all this code and was asked to implement the Serialize class. there r a few points:
1 - circular reference (of person)
2 - after Deserialize the mutual Address object must be the same again, as in only 1 instance.
so here is what i did:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.Data;
namespace SerializeExercise
{
class FullName
{
public bool Equals(FullName other)
{
return string.Equals(_firstName, other._firstName)
&& string.Equals(_lastName, other._lastName);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is FullName && Equals((FullName)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_firstName != null ? _firstName.GetHashCode() : 0) * 397)
^ (_lastName != null ? _lastName.GetHashCode() : 0);
}
}
public static bool operator ==(FullName left, FullName right)
{ return left.Equals(right); }
public static bool operator !=(FullName left, FullName right)
{ return !left.Equals(right); }
private string _firstName;
private string _lastName;
public FullName() { }
public FullName(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
public string FirstName
{ get { return _firstName; }
set { _firstName = value; } }
public string LastName
{ get { return _lastName; }
set { _lastName = value; } }
}
class Address
{
protected bool Equals(Address other)
{
return string.Equals(Street, other.Street)
&& string.Equals(City, other.City)
&& string.Equals(ZipCode, other.ZipCode)
&& string.Equals(PhoneNumber, other.PhoneNumber);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Address)obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = (Street != null ? Street.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (City != null ? City.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (ZipCode != null ? ZipCode.GetHashCode() : 0);
hashCode = (hashCode * 397)
^ (PhoneNumber != null ? PhoneNumber.GetHashCode() : 0);
return hashCode;
}
}
public static bool operator ==(Address left, Address right)
{ return Equals(left, right); }
public static bool operator !=(Address left, Address right)
{ return !Equals(left, right); }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string PhoneNumber { get; set; }
}
class Person
{
protected bool Equals(Person other)
{
return Name.Equals(other.Name) && Age == other.Age && Equals(Address, other.Address);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Person)obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = Name.GetHashCode();
hashCode = (hashCode * 397) ^ Age;
hashCode = (hashCode * 397) ^ (Address != null ? Address.GetHashCode() : 0);
return hashCode;
}
}
public static bool operator ==(Person left, Person right)
{ return Equals(left, right); }
public static bool operator !=(Person left, Person right)
{ return !Equals(left, right); }
public FullName Name { get; set; }
public int Age { get; set; }
public Address Address { get; set; }
public Person Spouse { get; set; }
}
class Program
{
static void Main(string[] args)
{
var alon = new Person
{
Name = new FullName("Alon", "Aloni"),
Age = 43,
Address = new Address
{
Street = "Streety 10",
City = "Big City",
ZipCode = "30500",
PhoneNumber = "+972-123456789"
}
};
var liat = new Person
{
Name = new FullName("Liat", "Liati"),
Age = 40,
Address = alon.Address,
Spouse = alon
};
alon.Spouse = liat;
using (var buffer = new MemoryStream())
{
Serializer.Serialize(liat, buffer);
buffer.Seek(0, SeekOrigin.Begin);
var newLiat = Serializer.Deserialize<Person>(buffer);
Trace.Assert(liat == newLiat, "Fail, object are not logicaly the same");
Trace.Assert(newLiat != null
&& newLiat.Spouse != null
&& newLiat.Spouse.Address != null
&& newLiat.Address != null
&& ReferenceEquals(newLiat.Address, newLiat.Spouse.Address),
"Address is not the same object");
Trace.Assert(newLiat != null
&& newLiat.Spouse != null
&& newLiat.Spouse.Spouse != null
&& ReferenceEquals(liat.Spouse.Spouse, liat),
"The new Liat and new Alon don't refer each other");
}
}
}
//and this is how i got Serializer:
public class Serializer
{
public static void Serialize<T>(T o, Stream stream) {}
public static T Deserialize<T>(Stream stream){}
}
}
so this is currently my solution - i am breaking all objects to table and create something similar to what i would have done if i would have wanted to save the data to a DB, serialize all that while putting the hashcode values wherever i need to see an object. in both Serialize and Deserialize i used dictionaries to manage the objects. i used dataset for comfort reasons.
public class Serializer
{ public static void Serialize<T>(T o, Stream stream)
{
var _2DDict = CreateDict2D(o);
DataSet ds = new DataSet("miniDB");
foreach (var entry in _2DDict)
{
Type t = entry.Key;
DataTable dt = new DataTable(t.FullName);
foreach (var p in t.GetProperties())
{
if (p.PropertyType.Namespace == o.GetType().Namespace)
dt.Columns.Add(p.Name, typeof(int)); // hashcode
else
dt.Columns.Add(p.Name, p.PropertyType);
}
ds.Tables.Add(dt);
foreach (var entry2 in entry.Value)
{
var newRow = dt.NewRow();
foreach (var p2 in entry2.Value.GetType().GetProperties())
{
if (p2.PropertyType.Namespace == o.GetType().Namespace)
newRow[p2.Name] = p2.GetValue(entry2.Value, null).GetHashCode();
else
newRow[p2.Name] = p2.GetValue(entry2.Value, null);
}
dt.Rows.Add(newRow);
}
}
ds.WriteXml(stream, XmlWriteMode.WriteSchema);
}
static Dictionary<Type, Dictionary<int, object>> CreateDict2D(object o)
{
var myTypes = o.GetType().GetProperties().Select(p => p.PropertyType)
.Where(p => p.IsClass).Distinct().ToList();
if (!myTypes.Contains(o.GetType()))
myTypes.Add(o.GetType());
var type_hashcode_object = new Dictionary<Type, Dictionary<int, object>>();
foreach (var t in myTypes)
type_hashcode_object.Add(t, new Dictionary<int, object>());
InsertMyFields(o, type_hashcode_object);
return type_hashcode_object;
}
static void InsertMyFields(object o,
Dictionary<Type, Dictionary<int, object>> type_hashcode_object)
{
var hashcode_typeDict = (Dictionary<int, object>)type_hashcode_object[o.GetType()];
if (!hashcode_typeDict.ContainsKey(o.GetHashCode()))
{
hashcode_typeDict.Add(o.GetHashCode(), o);
foreach (var p in o.GetType().GetProperties())
if (p.PropertyType.Namespace == o.GetType().Namespace)
InsertMyFields(p.GetValue(o, null), type_hashcode_object);
}
}
public static T Deserialize<T>(Stream stream)
{
DataSet ds = new DataSet();
ds.ReadXml(stream, XmlReadMode.ReadSchema);
var _2DDict = new Dictionary<Type, Dictionary<int, object>>();
foreach (DataTable tbl in ds.Tables)
_2DDict.Add(Type.GetType(tbl.TableName), new Dictionary<int, object>());
DataTable tblT = null;
foreach (DataTable tbl in ds.Tables)
{
if (tbl.TableName == typeof(T).FullName)
{
tblT = tbl;
continue;
}
foreach (DataRow r in tbl.Rows)
{
var obj = Activator.CreateInstance(Type.GetType(tbl.TableName));
foreach (DataColumn col in tbl.Columns)
obj.GetType().GetProperties().Where(p => p.Name == col.ColumnName)
.Single().SetValue(obj, r[col], null);
_2DDict[Type.GetType(tbl.TableName)].Add(obj.GetHashCode(), obj);
}
}
//var child_hashcode_T_dict = new Dictionary<int[], T>(); -- by theory...
var child_hashcode_T_dict = new Dictionary<int, T>();
var hashcode_T_dict = _2DDict[Type.GetType(tblT.TableName)];
T main = default(T);
var first = true;
foreach (DataRow r in tblT.Rows)
{
var obj = (T)Activator.CreateInstance(Type.GetType(tblT.TableName));
if (first)
{
first = false;
main = obj;
}
int child_hashcode = -1;
foreach (DataColumn col in tblT.Columns)
{
var prop = obj.GetType().GetProperties()
.Where(p => p.Name == col.ColumnName).Single();
if (prop.PropertyType.Namespace == typeof(T).Namespace)
{
if (prop.PropertyType == typeof(T))
{
child_hashcode = Convert.ToInt32(r[col]);
continue;
}
var requestedHashcode = Convert.ToInt32(r[col]);
prop.SetValue(obj, _2DDict[prop.PropertyType][requestedHashcode], null);
}
else
prop.SetValue(obj, r[col], null);
}
//_2DDict[Type.GetType(tblT.TableName)].Add(obj.GetHashCode(), obj);
hashcode_T_dict.Add(obj.GetHashCode(), obj);
child_hashcode_T_dict.Add(child_hashcode, obj);
}
foreach (var entry in child_hashcode_T_dict)
{
//var childProp = entry.Value.GetType().GetProperties()
.Where(cp => cp.PropertyType == typeof(T));
//in theory i should be able to do foreach...
var childProp = entry.Value.GetType().GetProperties()
.Where(cp => cp.PropertyType == typeof(T)).Single();
childProp.SetValue(entry.Value, hashcode_T_dict[entry.Key], null);
}
return main;
}
}
1 - circular reference (of person)
2 - after Deserialize the mutual Address object must be the same again, as in only 1 instance.
so here is what i did:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.Data;
namespace SerializeExercise
{
class FullName
{
public bool Equals(FullName other)
{
return string.Equals(_firstName, other._firstName)
&& string.Equals(_lastName, other._lastName);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is FullName && Equals((FullName)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_firstName != null ? _firstName.GetHashCode() : 0) * 397)
^ (_lastName != null ? _lastName.GetHashCode() : 0);
}
}
public static bool operator ==(FullName left, FullName right)
{ return left.Equals(right); }
public static bool operator !=(FullName left, FullName right)
{ return !left.Equals(right); }
private string _firstName;
private string _lastName;
public FullName() { }
public FullName(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
public string FirstName
{ get { return _firstName; }
set { _firstName = value; } }
public string LastName
{ get { return _lastName; }
set { _lastName = value; } }
}
class Address
{
protected bool Equals(Address other)
{
return string.Equals(Street, other.Street)
&& string.Equals(City, other.City)
&& string.Equals(ZipCode, other.ZipCode)
&& string.Equals(PhoneNumber, other.PhoneNumber);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Address)obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = (Street != null ? Street.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (City != null ? City.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (ZipCode != null ? ZipCode.GetHashCode() : 0);
hashCode = (hashCode * 397)
^ (PhoneNumber != null ? PhoneNumber.GetHashCode() : 0);
return hashCode;
}
}
public static bool operator ==(Address left, Address right)
{ return Equals(left, right); }
public static bool operator !=(Address left, Address right)
{ return !Equals(left, right); }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string PhoneNumber { get; set; }
}
class Person
{
protected bool Equals(Person other)
{
return Name.Equals(other.Name) && Age == other.Age && Equals(Address, other.Address);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Person)obj);
}
public override int GetHashCode()
{
unchecked
{
int hashCode = Name.GetHashCode();
hashCode = (hashCode * 397) ^ Age;
hashCode = (hashCode * 397) ^ (Address != null ? Address.GetHashCode() : 0);
return hashCode;
}
}
public static bool operator ==(Person left, Person right)
{ return Equals(left, right); }
public static bool operator !=(Person left, Person right)
{ return !Equals(left, right); }
public FullName Name { get; set; }
public int Age { get; set; }
public Address Address { get; set; }
public Person Spouse { get; set; }
}
class Program
{
static void Main(string[] args)
{
var alon = new Person
{
Name = new FullName("Alon", "Aloni"),
Age = 43,
Address = new Address
{
Street = "Streety 10",
City = "Big City",
ZipCode = "30500",
PhoneNumber = "+972-123456789"
}
};
var liat = new Person
{
Name = new FullName("Liat", "Liati"),
Age = 40,
Address = alon.Address,
Spouse = alon
};
alon.Spouse = liat;
using (var buffer = new MemoryStream())
{
Serializer.Serialize(liat, buffer);
buffer.Seek(0, SeekOrigin.Begin);
var newLiat = Serializer.Deserialize<Person>(buffer);
Trace.Assert(liat == newLiat, "Fail, object are not logicaly the same");
Trace.Assert(newLiat != null
&& newLiat.Spouse != null
&& newLiat.Spouse.Address != null
&& newLiat.Address != null
&& ReferenceEquals(newLiat.Address, newLiat.Spouse.Address),
"Address is not the same object");
Trace.Assert(newLiat != null
&& newLiat.Spouse != null
&& newLiat.Spouse.Spouse != null
&& ReferenceEquals(liat.Spouse.Spouse, liat),
"The new Liat and new Alon don't refer each other");
}
}
}
//and this is how i got Serializer:
public class Serializer
{
public static void Serialize<T>(T o, Stream stream) {}
public static T Deserialize<T>(Stream stream){}
}
}
so this is currently my solution - i am breaking all objects to table and create something similar to what i would have done if i would have wanted to save the data to a DB, serialize all that while putting the hashcode values wherever i need to see an object. in both Serialize and Deserialize i used dictionaries to manage the objects. i used dataset for comfort reasons.
public class Serializer
{ public static void Serialize<T>(T o, Stream stream)
{
var _2DDict = CreateDict2D(o);
DataSet ds = new DataSet("miniDB");
foreach (var entry in _2DDict)
{
Type t = entry.Key;
DataTable dt = new DataTable(t.FullName);
foreach (var p in t.GetProperties())
{
if (p.PropertyType.Namespace == o.GetType().Namespace)
dt.Columns.Add(p.Name, typeof(int)); // hashcode
else
dt.Columns.Add(p.Name, p.PropertyType);
}
ds.Tables.Add(dt);
foreach (var entry2 in entry.Value)
{
var newRow = dt.NewRow();
foreach (var p2 in entry2.Value.GetType().GetProperties())
{
if (p2.PropertyType.Namespace == o.GetType().Namespace)
newRow[p2.Name] = p2.GetValue(entry2.Value, null).GetHashCode();
else
newRow[p2.Name] = p2.GetValue(entry2.Value, null);
}
dt.Rows.Add(newRow);
}
}
ds.WriteXml(stream, XmlWriteMode.WriteSchema);
}
static Dictionary<Type, Dictionary<int, object>> CreateDict2D(object o)
{
var myTypes = o.GetType().GetProperties().Select(p => p.PropertyType)
.Where(p => p.IsClass).Distinct().ToList();
if (!myTypes.Contains(o.GetType()))
myTypes.Add(o.GetType());
var type_hashcode_object = new Dictionary<Type, Dictionary<int, object>>();
foreach (var t in myTypes)
type_hashcode_object.Add(t, new Dictionary<int, object>());
InsertMyFields(o, type_hashcode_object);
return type_hashcode_object;
}
static void InsertMyFields(object o,
Dictionary<Type, Dictionary<int, object>> type_hashcode_object)
{
var hashcode_typeDict = (Dictionary<int, object>)type_hashcode_object[o.GetType()];
if (!hashcode_typeDict.ContainsKey(o.GetHashCode()))
{
hashcode_typeDict.Add(o.GetHashCode(), o);
foreach (var p in o.GetType().GetProperties())
if (p.PropertyType.Namespace == o.GetType().Namespace)
InsertMyFields(p.GetValue(o, null), type_hashcode_object);
}
}
public static T Deserialize<T>(Stream stream)
{
DataSet ds = new DataSet();
ds.ReadXml(stream, XmlReadMode.ReadSchema);
var _2DDict = new Dictionary<Type, Dictionary<int, object>>();
foreach (DataTable tbl in ds.Tables)
_2DDict.Add(Type.GetType(tbl.TableName), new Dictionary<int, object>());
DataTable tblT = null;
foreach (DataTable tbl in ds.Tables)
{
if (tbl.TableName == typeof(T).FullName)
{
tblT = tbl;
continue;
}
foreach (DataRow r in tbl.Rows)
{
var obj = Activator.CreateInstance(Type.GetType(tbl.TableName));
foreach (DataColumn col in tbl.Columns)
obj.GetType().GetProperties().Where(p => p.Name == col.ColumnName)
.Single().SetValue(obj, r[col], null);
_2DDict[Type.GetType(tbl.TableName)].Add(obj.GetHashCode(), obj);
}
}
//var child_hashcode_T_dict = new Dictionary<int[], T>(); -- by theory...
var child_hashcode_T_dict = new Dictionary<int, T>();
var hashcode_T_dict = _2DDict[Type.GetType(tblT.TableName)];
T main = default(T);
var first = true;
foreach (DataRow r in tblT.Rows)
{
var obj = (T)Activator.CreateInstance(Type.GetType(tblT.TableName));
if (first)
{
first = false;
main = obj;
}
int child_hashcode = -1;
foreach (DataColumn col in tblT.Columns)
{
var prop = obj.GetType().GetProperties()
.Where(p => p.Name == col.ColumnName).Single();
if (prop.PropertyType.Namespace == typeof(T).Namespace)
{
if (prop.PropertyType == typeof(T))
{
child_hashcode = Convert.ToInt32(r[col]);
continue;
}
var requestedHashcode = Convert.ToInt32(r[col]);
prop.SetValue(obj, _2DDict[prop.PropertyType][requestedHashcode], null);
}
else
prop.SetValue(obj, r[col], null);
}
//_2DDict[Type.GetType(tblT.TableName)].Add(obj.GetHashCode(), obj);
hashcode_T_dict.Add(obj.GetHashCode(), obj);
child_hashcode_T_dict.Add(child_hashcode, obj);
}
foreach (var entry in child_hashcode_T_dict)
{
//var childProp = entry.Value.GetType().GetProperties()
.Where(cp => cp.PropertyType == typeof(T));
//in theory i should be able to do foreach...
var childProp = entry.Value.GetType().GetProperties()
.Where(cp => cp.PropertyType == typeof(T)).Single();
childProp.SetValue(entry.Value, hashcode_T_dict[entry.Key], null);
}
return main;
}
}
Comments
Post a Comment