First of all had a good look around and understand override/virtual etc. But haven't found any cases specific to my situation - which I'm sure isn't unique. I want to just make sure the implementation I go with is the right implementation. I have the following code setup to demonstrate my issue:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Sandpit
{
public class Program
{
static void Main()
{
var fixture = new Fixture
{
Name = "Fixture Name",
Participants = new List<Participant> {new Participant {Name = "Participant Name"}}
};
var writer = new StringWriter(new StringBuilder());
var serializer = new JsonSerializer();
serializer.Converters.Add(new StringEnumConverter());
serializer.Serialize(writer, fixture);
Console.Write(writer.ToString());
Console.ReadKey();
}
}
public class Fixture
{
public string Name { get; set; }
public List<Participant> Participants { get; set; }
public override bool Equals(object obj)
{
var fixture = (Fixture)obj;
return fixture.Name == Name;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
public class Participant
{
public string Name { get; set; }
public override bool Equals(object obj)
{
var participant = (Participant)obj;
return participant.Name == Name;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
}
Now when this runs I get an exception on var fixture = (Fixture)obj;.
Unable to cast object of type
'System.Collections.Generic.List`1[Sandpit.Participant]' to type
'Sandpit.Fixture'.
I don't understand why it is getting into there. And why this breaks the correct implementation of overridden object methods.
I know that I can fix this by doing public new bool Equals(object obj). Am I doing this right? Also these objects are well integrated into the application I am working on, is there likely to be any side effects to making this change?
Many thanks,
Matt
A small change to your Fixture and Participant classes fixes this:
public class Fixture
{
public string Name { get; set; }
public List<Participant> Participants { get; set; }
public override bool Equals(object obj)
{
var fixture = obj as Fixture;
return fixture == null ? false : fixture.Name == Name;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
public class Participant
{
public string Name { get; set; }
public override bool Equals(object obj)
{
var participant = obj as Participant;
return participant == null ? false : participant.Name == Name;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
If you are comparing to an element that's of another type, you can be certain that the two are not equal.
Related
So i have an interface named IResource that consist of those 5 properties as readonly but since I am adding those to a Dictionary<IResource, int> i need a way to compare two IResource so i don't have duplicates in the Dictionary. Is there a way for me to add a default Equals(object obj) to every IResource?
I already added an Equals Override to the Wood class and it solved the problem but i would have to add a Equals(object obj) in every class that implements the IResource.
public class Wood : IResource
{
public string Package => "Core";
public string Family => "Wood";
public string Name => "Wood";
public bool IsFractal => false;
public ResourceType Type => ResourceType.Natural;
}
PS:I have an override of the Add(IResource key, uint value) method to the dictionary to check if the IResource already exists.
public new void Add(IResource key, uint value)
{
if (base.ContainsKey(key))
base[key] += value;
else
base.Add(key, value);
}
Right now when i add a IResource interface to the dictionary, it always adds a new entry.
You can move your comparison to a base class and override Equals and GetHashCode there. Just add any members you want to use in the comparison to the abstract class and include them in the equity comparison.
For example:
public enum ResourceType { Natural }
public interface IResource
{
public string Name { get; }
public ResourceType ResourceType { get; }
}
public abstract class Resource
{
public abstract string Name { get; }
public abstract ResourceType ResourceType { get; }
// other properties that you want to use for your resource comparision
public override bool Equals(object obj)
=> obj is Resource r && Name == r.Name && ResourceType == r.ResourceType;
public override int GetHashCode() => (Name, ResourceType).GetHashCode();
}
public class Wood : Resource, IResource
{
public override string Name => "Wood";
public override ResourceType ResourceType => ResourceType.Natural;
// ... other properties
}
While it's certainly possible to create an abstract base class - as others have pointed out - it's really not a great idea. You're creating a dependency that any class that implements IResource must also implement equality as you've defined it for IResource. And that might be fine or it might make it hard to maintain and lead to bugs.
The framework is designed to handle this situation by allowing you to customize how the dictionary does comparisons. It does this by using IEqualityComparer.
Here's an example for your IResource interface:
public class ResourceComparer : IEqualityComparer<IResource>
{
public bool Equals([AllowNull] IResource x, [AllowNull] IResource y)
{
if (null == x && null == y)
return true;
if (null == x || null == y)
return false;
return x.Package.Equals(y.Package) &&
x.Family.Equals(y.Family) &&
x.Name.Equals(y.Name) &&
x.IsFractal.Equals(y.IsFractal) &&
x.Type.Equals(y.Type);
}
public int GetHashCode([DisallowNull] IResource obj)
{
HashCode hash = new HashCode();
hash.Add(obj.Package);
hash.Add(obj.Family);
hash.Add(obj.Name);
hash.Add(obj.IsFractal);
hash.Add(obj.Type);
return hash.ToHashCode();
}
}
Once you've got that then you can create your dictionary with that comparer. I've used your Wood class and created one other called Metal. Neither has to share a base class or override Equals and GetHashCode.
static void Main(string[] _)
{
var resourceMap = new Dictionary<IResource,uint>(new ResourceComparer());
var resources = new IResource[] { new Wood(), new Metal(),
new Wood(), new Wood() };
foreach (var r in resources)
{
if (resourceMap.TryGetValue(r, out var count))
resourceMap[r] = count + 1;
else
resourceMap.Add(r, 1);
}
Console.WriteLine(resourceMap[new Wood()]);
Console.WriteLine(resourceMap[new Metal()]);
}
Here's the simple POCO style metal class:
public class Metal : IResource
{
public string Package => "Core";
public string Family => "Metal";
public string Name => "Metal";
public bool IsFractal => false;
public ResourceType Type => ResourceType.ManMade;
}
You can create an abstract class that implements IResource. Use that class and override Equals and GetHashCode.
public abstract class Resource : IResource
{
//make all your interface properties abstract
public abstract string Package { get; }
public abstract string Family { get; }
public abstract string Name { get; }
public abstract bool IsFractal { get; }
public abstract ResourceType Type { get; }
public override bool Equals(object obj)
{
if (!(obj is Resource resource)) return false;
return ReferenceEquals(this, resource) ||
Package == resource.Package &&
Family == resource.Family &&
Name == resource.Family &&
IsFractal == resource.IsFractal &&
Type == resource.Type;
}
public override int GetHashCode()
{
return HashCode.Combine(Package, Family, Name, IsFractal, Type);
}
}
Then make all your resources implement the abstract class Resource
public class Wood : Resource
{
public override string Package => "Core";
public override string Family => "Wood";
public override string Name => "Wood";
public override bool IsFractal => false;
public override ResourceType Type => ResourceType.Natural;
}
public class Rock : Resource
{
public override string Package => "Core";
public override string Family => "Rock";
public override string Name => "Rock";
public override bool IsFractal => false;
public override ResourceType Type => ResourceType.Natural;
}
This will give you the behavior you expect.
I am trying to serialize custom EntityData class into Eyeshot proprietary file format. There is a great article about this (https://devdept.zendesk.com/hc/en-us/articles/360003318873-Eyeshot-Proprietary-File-Format),
it works fine if I serialize base class, however I can't serialize a class that is derived from my base class. Here is a sample, I tried to keep it as small as possible(please read comments along the way):
public class BaseClass
{
public int Id { get; set; }
public virtual BaseClassSurrogate ConvertToSurrogate() { return new BaseClassSurrogate(this); }
}
public class BaseClassSurrogate : Surrogate<BaseClass>
{
public BaseClassSurrogate(BaseClass myBaseClass) : base(myBaseClass) { }
public int Id { get; set; }
protected override BaseClass ConvertToObject()
{
var baseClass = new BaseClass();
CopyDataToObject(baseClass);
return baseClass;
}
protected override void CopyDataFromObject(BaseClass obj) { Id = obj.Id; }
protected override void CopyDataToObject(BaseClass obj) { obj.Id = this.Id; }
public static implicit operator BaseClass(BaseClassSurrogate surrogate) { return surrogate?.ConvertToObject(); }
public static implicit operator BaseClassSurrogate(BaseClass source) { return source?.ConvertToSurrogate(); }
}
And my derived class with its surrogate implementation:
public class DerivedClass : BaseClass
{
public int Number { get; set; }
public override BaseClassSurrogate ConvertToSurrogate() { return new DerivedClassSurrogate(this); }
}
public class DerivedClassSurrogate : BaseClassSurrogate
{
public DerivedClassSurrogate(DerivedClass baseClass) : base(baseClass) { }
public int Number { get; set; }
protected override BaseClass ConvertToObject()
{
var derivedClass= new DerivedClass();
CopyDataToObject(derivedClass);
return derivedClass;
}
protected override void CopyDataFromObject(BaseClass obj)
{
if (obj is DerivedClass derivedClass)
Number = derivedClass.Number;
base.CopyDataFromObject(obj);
}
protected override void CopyDataToObject(BaseClass obj)
{
if (obj is DerivedClass derivedClass)
derivedClass.Number = Number;
base.CopyDataToObject(obj);
}
//I don't understand do I need to call these in derived class as well?
//public static implicit operator BaseClass(BaseClassSurrogate surrogate) { return surrogate?.ConvertToObject(); }
//public static implicit operator BaseClassSurrogate(BaseClass source) { return source?.ConvertToSurrogate(); }
}
And here is FillModel method from FileSerializer class:
protected override void FillModel()
{
base.FillModel();
Model.Add(typeof(BaseClass), false)
.SetSurrogate(typeof(BaseClassSurrogate));
MetaType mt1 = Model[typeof(BaseClassSurrogate)]
.Add(1, "Id");
mt1.SetCallbacks(null, null, "BeforeDeserialize", null);
mt1.UseConstructor = false;
Model.Add(typeof(DerivedClass), false)
.SetSurrogate(typeof(DerivedClassSurrogate));
MetaType mt2 = Model[typeof(DerivedClassSurrogate)]
.Add(1, "Number");
mt2.SetCallbacks(null, null, "BeforeDeserialize", null);
mt2.UseConstructor = false;
}
This code gives me error:"No suitable conversion operator found for surrogate DerivedClass/DerivedClassSurrogate". Any help would be highly appreciated.
In FillModel() method you forgot to specify the hierarchy for your custom classes, try in this way:
protected override void FillModel()
{
base.FillModel();
Model.Add(typeof(BaseClass), false)
.AddSubType(1001, typeof(DerivedClass))
.SetSurrogate(typeof(BaseClassSurrogate));
Model[typeof(BaseClassSurrogate)]
.AddSubType(1001, typeof(DerivedClassSurrogate))
.Add(1, "Id")
.SetCallbacks(null, null, "BeforeDeserialize", null)
.UseConstructor = false;
Model[typeof(DerivedClassSurrogate)]
.Add(1, "Number")
.UseConstructor = false;
}
Trying to serialize a collection of a custom type with an overloaded Equals(object obj) method. I am using Newtonsoft.Json.JsonConvert.SerializeObject(object value) to achieve this.
This is my abstract base view model from which the view model in question inherits:
public abstract class BaseCollectibleViewModel
{
protected abstract bool CompareParameters(object item);
protected abstract List<int> GetParameters();
public override bool Equals(object obj)
{
if (CompareParameters(obj))
{
return true;
}
return false;
}
public override int GetHashCode()
{
int hash = 13;
foreach (var parameter in GetParameters())
{
hash = (hash * 7) + parameter.GetHashCode();
}
return hash;
}
public static bool operator ==(BaseCollectibleViewModel a, BaseCollectibleViewModel b)
{
if (a.Equals(b))
{
return true;
}
return false;
}
public static bool operator !=(BaseCollectibleViewModel a, BaseCollectibleViewModel b)
{
if (a.Equals(b))
{
return false;
}
return true;
}
}
This is the actual view model:
public class ImagesViewModel : BaseCollectibleViewModel, ISourceImage
{
public string Name { get; private set; }
public string Type { get; private set; }
[ScriptIgnore]
public Stream Content { get; private set; }
[ScriptIgnore]
private HttpPostedFileBase _file;
[ScriptIgnore]
public HttpPostedFileBase File
{
get
{
return _file;
}
set
{
_file = value;
Name = File.FileName;
Type = File.ContentType;
Content = new MemoryStream();
File.InputStream.CopyTo(Content);
}
}
protected override bool CompareParameters(object obj)
{
var temp = obj as ImagesViewModel;
if(temp == null)
{
return false;
}
return
(Name == temp.Name &&
Type == temp.Type);
}
protected override List<int> GetParameters()
{
return new List<int>()
{
Name.GetHashCode(),
Type.GetHashCode()
};
}
}
Notice the ScriptIgnore attributes. I even have one on the private field. The program breaks on the == operator of the base class because both of the arguments that get passed are null.
This is the serializing code:
[HttpPost]
public string GetSessionImages()
{
var imagesInSession = _imagesSessionService.GetCollection();
return JsonConvert.SerializeObject(imagesInSession, Formatting.Indented);
}
Also this:
The screenshot is showing the implementation of the abstract CompareParameters(object obj) method on the inheriting view model. That stream is the Content property stream, I have checked. Why is this happening?
EDIT: When not overriding Equals I get a JsonSerializationException stating:
{"Error getting value from 'ReadTimeout' on
'System.IO.MemoryStream'."}
EDIT 2: Per dbc's comment I have replaced the attribute [ScriptIgnore] with [JsonIgnore] and the code worked to an extent.
However, I had to comment out the operator implementations because the '==' operator would be passed a null value as the BaseCollectibleViewModel b argument.
Since you are using json.net, you must mark members to ignore with [JsonIgnore]:
using Newtonsoft.Json;
public class ImagesViewModel : BaseCollectibleViewModel, ISourceImage
{
public string Name { get; private set; }
public string Type { get; private set; }
[ScriptIgnore]
[JsonIgnore]
public Stream Content { get; private set; }
[ScriptIgnore]
[JsonIgnore]
public HttpPostedFileBase File { get { ... } set { ... } }
It is not necessary to mark entirely private members with [JsonIgnore] as these are not serialized by default by Json.NET.
Alternatively, if you do not want your models to have a dependency on Json.NET, you could use conditional property serialization to unconditionally suppress the same members:
public class ImagesViewModel : BaseCollectibleViewModel, ISourceImage
{
public string Name { get; private set; }
public string Type { get; private set; }
[ScriptIgnore]
public Stream Content { get; private set; }
public bool ShouldSerializeContent() { return false; }
[ScriptIgnore]
public HttpPostedFileBase File { get { ... } set { ... } }
public bool ShouldSerializeFile() { return false; }
Note that the ShouldSerializeXXX() conditional serialization pattern is also respected by other serializers including XmlSerializer, as explained in ShouldSerialize*() vs *Specified Conditional Serialization Pattern - a side-effect which may be desirable, or not.
(Incidentally, you might want to check that you are not double-serializing your data as shown in JSON.NET Parser *seems* to be double serializing my objects.)
A parent type:
public class IdObject : IComparable<IdObject>, IEquatable<IdObject>
{
public int id { get; set; }
public bool Equals(IdObject other)
{
if (other == null) return this == null;
if (this == null) return false;
var test = other.id.CompareTo(this.id);
return other.id.CompareTo(this.id) == 0;
}
public int CompareTo(IdObject other)
{
return other.id.CompareTo(this.id);
}
}
A child:
public class NamedObject : IdObject
{
public string name { get; set; }
}
Comparing lists of IdObjects
var list1 = new List<IdObject>()
{
new IdObject() { id = 42 },
new IdObject() { id = 43 }
};
var list2 = new List<IdObject>()
{
new IdObject() { id = 43 },
new IdObject() { id = 42 }
};
list1.Sort();
list2.Sort();
var test = list1.SequenceEqual(list2); // True
Comparing lists of Nameds
var list1 = new List<NamedObject>()
{
new NamedObject() { id = 42 },
new NamedObject() { id = 43 }
};
var list2 = new List<NamedObject>()
{
new NamedObject() { id = 43 },
new NamedObject() { id = 42 }
};
list1.Sort();
list2.Sort();
var test = list1.SequenceEqual(list2); // False
I realized that IdObject::Equals is not called through a NamedObject context.
Am I doing something wrong ?
Isn't supposed to call the inherited Equals ?
How can I use the parent's Equals ?
Basically, you've got a problem because your type doesn't override object.Equals(object) in a way consistent with your IEquatable<T> implementation and you're dealing with a collection of the subclasses.
SequenceEqual will be using EqualityComparer<NamedObject>.Default. That will check whether NamedObject implements IEquatable<NamedObject> - and will find that it doesn't, so it will fall back to calling object.Equals(object). You can see this here:
using System;
using System.Collections.Generic;
public class Base : IEquatable<Base>
{
public override bool Equals(object other)
{
Console.WriteLine("Equals(object)");
return false;
}
public bool Equals(Base other)
{
Console.WriteLine("Equals(Base)");
return false;
}
public override int GetHashCode() => 0;
}
public class Derived : Base
{
}
public class Test
{
static void Main()
{
var comparer = EqualityComparer<Derived>.Default;
Console.WriteLine(comparer.Equals(new Derived(), new Derived()));
}
}
You don't override object.Equals(object), so you've effectively got reference equality.
I would recommend that you override object.Equals(object) and object.GetHashCode() in your base class.
You could then also implement IEquatable<NamedObject> in NamedObject, just delegating to the base implementation or (better) checking the name as well, unless you really don't want that to be taken into account.
check that I have a customAttribute called ResponseAttributes. I have declared an interface and several concrete classes { Question 1, Question2, Question3 }
[AttributeUsage(AttributeTargets.Property)]
public class ResponseAttribute : Attribute { }
public interface IQuestion { }
public class Question1 : IQuestion
{
[Response]
public string Response { get; set; }
public Question1() { Response = "2+1"; }
}
public class Question2 : IQuestion
{
[Response]
public decimal Response { get; set; }
public Question2() { Response = 5; }
}
public class Question3 : IQuestion
{
[Response]
public string Response { get; set; }
public Question3() { Response = "2+1"; }
}
Now, what I'm trying to do is how to verify a class which contains that attribute is equals to another?
I mean:
List<IQuestion> questions = new List<IQuestion>()
{
new Question1(), new Question2()
};
Question3 question3 = new Question3();
foreach (var question in questions)
{
// how to verify this condition:
// if (customAttribute from question3 is equals to customAttribute fromquestion)
// of course the question3 is equals to 1
}
As you can, they are different types, that's the reason why I set as ResponseAttribute.
you could try using an interface, with Resposnse property (type object)
if you can not, you could use a class-level attribute that tell you the "Response property" than, you can use reflection on that property
Example:
public class ResponseAttribute : Attribute {
public string PropertyName { get; set }
}
[ResponseAttribute ("CustomResponse")}
public class Question1 {
public string CustomResponse;
}
via reflection
foreach(var question in questions) {
var responseAttr = (ResponseAttribute) question.GetType().GetCustomAttributes(typeof(ResponseAttribute));
var questionResponse= question.GetType().GetProperty(responseAttr.PropertyName,question,null);
}
try overriding equals method:
public override bool Equals(object obj)
{
if (obj is IQuestion)
return this.Response == ((IQuestion)obj).Response;
else
return base.Equals(obj);
}
hope this helps