How to structure classes to Implement IEquatable and ISerializable
-
27-09-2019 - |
문제
Been banging my head on this for a while now
The problem I have is trying to add the IEquatable behaviour so my derived classes can use set operations Intersect of ILink etc.
At the moment I have...
public interface ILink
{
int Linkid { get; set; }
bool IsActive { get; set; }
}
and a bunch of derived classes like
public class Domain : ILink
{
public Domain(){}
}
public class User : ILink
{
public User (){}
}
so in order to do Intersection of List I thought I'd create an abstract class like so...
public abstract class AbstractLink : IEquatable<ILink>, ISerializable, ILink
{
public AbstractLink(){}
public AbstractLink(SerializationInfo info, StreamingContext ctxt)
{}
public virtual void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{}
}
however when I change my derived types from
public class DomainLink : ILink
{
}
to
public class DomainLink : AbstractLink
{
}
I get a SerializationException "Member 'Linkid' was not found." which is the 1st member it attempts to deserialize
BTW: this is Remoting hence the need for the custom Serialization - is there a way to compose these behaviours together?
Many thanks!
M
해결책
Your example code does not compile. You do not implement the ILink interface members.
The following code does work.
Each object that overrides AbstractLink requires a serialization constructor. Each subclass for AbstractLink also needs to be annotated with [Serializable]. If you add extra properties to the domain objects you will also need to implement GetObjectData() for the extra properties.
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
public interface ILink
{
int Linkid { get; set; }
bool IsActive { get; set; }
}
[Serializable]
public class Domain : AbstractLink
{
public Domain()
{
}
public Domain(SerializationInfo info, StreamingContext ctx)
: base(info, ctx)
{
}
}
[Serializable]
public class User : AbstractLink
{
public string UserName { get; set; }
public User()
{
}
public User(SerializationInfo info, StreamingContext ctx)
: base(info, ctx)
{
UserName = info.GetString("UserName");
}
public override void GetObjectData(SerializationInfo info, StreamingContext ctx)
{
base.GetObjectData(info, ctx);
info.AddValue("UserName", UserName);
}
}
public abstract class AbstractLink : ISerializable, ILink, IEquatable<ILink>
{
public AbstractLink() { }
public AbstractLink(SerializationInfo info, StreamingContext ctx)
{
Linkid = info.GetInt32("Linkid");
IsActive = info.GetBoolean("IsActive");
}
public virtual void GetObjectData(SerializationInfo info, StreamingContext ctx)
{
info.AddValue("Linkid", Linkid);
info.AddValue("IsActive", IsActive);
}
public bool Equals(ILink other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return other.Linkid == Linkid && other.IsActive.Equals(IsActive);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
return obj.GetType() == typeof(AbstractLink) && Equals((AbstractLink) obj);
}
public override int GetHashCode()
{
unchecked
{
return (Linkid * 397) ^ IsActive.GetHashCode();
}
}
public int Linkid { get; set; }
public bool IsActive { get; set; }
}
class Program
{
static void Main(string[] args)
{
var user = new User { UserName = "user", IsActive = true, Linkid = 1 };
User user2;
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, user);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
user2 = bf.Deserialize(ms) as User;
}
Debug.Assert(user2 != null);
Debug.Assert(ReferenceEquals(user, user2) == false);
Debug.Assert(Equals(user.Linkid, user2.Linkid));
Debug.Assert(Equals(user.IsActive, user2.IsActive));
Debug.Assert(Equals(user.UserName, user2.UserName));
}
}