I have a code that it was use the record to define a member .But I have another project in lower version of .net core , it seems can not use record . How can I change it with class?
origin :
public record BucketObject(string name, string urn);
can be changed with :
public class BucketObject
{
public BucketObject(string _name, string _urn)
{
_name = name;
_urn = urn;
}
public string name { get; set; }
public string urn { get; set; }
};
First of all, the easiest way would be to add <LangVersion>latest</LangVersion> to your project file and add a IsExternalInit class.
namespace System.Runtime.CompilerServices
{
using System.ComponentModel;
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit
{
}
}
If that is not an option the corresponding class would be
public class BucketObject : IEquatable<BucketObject>
{
public BucketObject(string name, string urn)
{
Name = name;
Urn = urn;
}
public string Name { get; }
public string Urn { get; }
public bool Equals(BucketObject other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Name == other.Name && Urn == other.Urn;
}
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((BucketObject)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((Name != null ? Name.GetHashCode() : 0) * 397) ^ (Urn != null ? Urn.GetHashCode() : 0);
}
}
public static bool operator ==(BucketObject left, BucketObject right) => Equals(left, right);
public static bool operator !=(BucketObject left, BucketObject right) => !Equals(left, right);
};
You might note that most of the code is in the equality comparison methods, these are one of the things the records get you for free. You might also note that the object is immutable, so you do not have to worry about the object changing, or the difference between class/struct.
You will also be missing out on the myBucket with {Name = "newName"} syntax, and that is just unavoidable as far as I know.
You might also just consider upgrading your projects. Life tend to be easier if you keep all the projects at the same version. And if you are already on .net core, updating should be fairly painless.
Related
This question already has answers here:
IEquatable, how to implement this properly [duplicate]
(2 answers)
Closed 2 years ago.
I have a class heirarchy as follows:
public abstract class Issue
{
// some base properties
// these are not included in equality checks for deriving classes
// also not performing equality checks on this base class (it is abstract anyway)
}
public class IssueTypeA : Issue, IEquatable<IssueTypeA>
{
// some properties specific to this class
// these are the two for which equality comparison is performed
public string requirement { get; set; }
public string preamble { get; set; }
public bool Equals(IssueTypeA that)
{
// determine based on the values of the properties
// they must both be the same for equality
if ((this.requirement.Equals(that.requirement)) &&
(this.preamble.Equals(that.preamble)))
{
return true;
}
return false;
}
}
public class IssueTypeB : Issue, IEquatable<IssueTypeB>
{
// some properties specific to this class
public bool Equals(IssueTypeB that)
{
// determine based on the values of the properties
}
}
There is another class, intended to receive objects in the above heirarchy (except the base class, of course), and do some comparison operations with them:
public class Comparer<T> where T : Issue
{
// various comparison methods
public IEnumerable<T> getReferenceChangedIssues(IEnumerable<T> spreadsheetIssues, IEnumerable<T> downloadedIssues)
{
foreach (T spreadsheetRecord in T spreadsheetIssues)
{
foreach (T downloadedIssue in downloadedIssues)
{
// this is the point of failure
// there are cases in which this should be true, but it is not
if (spreadsheetRecord.Equals(downloadedIssue))
{
// the referenceChanged method works fine by itself
// it has been unit tested
if (spreadsheetRecord.referenceChanged(downloadedIssue))
yield return spreadsheetRecord;
}
}
}
}
}
Through unit testing and debugging, it's apparent that the custom Equals methods defined above are not being used correctly by Comparer. Why is this? It is also intended to include methods with some set operations in the future, which will need Equals.
When you implement IEquatable, you really need to override bool Equals(object) and GetHashCode. If you don't do that, things will break in weird ways. This was the stripped-down-est thing I could come up with. I think it should solve your issue (but I can't really tell without your comparison code). If folks point out issues, I'll fix up the code:
public abstract class Issue : IEquatable<Issue>
{
// possibly (not not necessarily) some base properties
// if they are included, make them part of the equality comparison and
// include them in the HashCode calculations
public bool Equals(Issue other)
{
if (other == null)
{
return false;
}
if (this.GetType() != other.GetType())
{
return false;
}
//test local properties and return true/false based on their values
return true;
}
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((Issue) obj);
}
}
public class IssueTypeA : Issue, IEquatable<IssueTypeA>
{
public string AName { get; set; } = "My A Name";
public int AValue { get; set; } = 42;
public bool Equals(IssueTypeA other)
{
if (!base.Equals(other))
{
return false;
}
return AName == other.AName && AValue == other.AValue;
}
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((IssueTypeA) obj);
}
//hash-codes need to be consistent once they are calculated - jump through some hoops
private int? _hashCodeValue = default;
public override int GetHashCode()
{
if (!_hashCodeValue.HasValue)
{
_hashCodeValue = $"{AName}{AValue}".GetHashCode();
}
return _hashCodeValue.Value;
}
}
public class IssueTypeB : Issue, IEquatable<IssueTypeB>
{
// some properties specific to this class
public string BName { get; set; } = "My B Name";
public int BValue { get; set; } = 84;
public bool Equals(IssueTypeB other)
{
if (!base.Equals(other))
{
return false;
}
//calculate equality based on local properties and return true/false
return BName == other.BName && BValue == other.BValue;
}
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((IssueTypeB) obj);
}
//hash-codes need to be consistent once they are calculated - jump through some hoops
private int? _hashCodeValue = default;
public override int GetHashCode()
{
if (!_hashCodeValue.HasValue)
{
_hashCodeValue = $"{BName}{BValue}".GetHashCode();
}
return _hashCodeValue.Value;
}
}
This is a better stab at a solution than the first pass. I check for equality based on properties local to each class. I also provide a GetHashCode implementation that is consistent (in the weird way that I believe GetHashCode requires - but in a way that drive Resharper nuts).
Why does this program print "not added" while I think it should print "added"?
using System;
using System.Collections.Generic;
class Element
{
public int id;
public Element(int id)
{
this.id = id;
}
public static implicit operator Element(int d)
{
Element ret = new Element(d);
return ret;
}
public static bool operator ==(Element e1, Element e2)
{
return (e1.id == e2.id);
}
public static bool operator !=(Element e1, Element e2)
{
return !(e1.id == e2.id);
}
}
class MainClass
{
public static void Main(string[] args)
{
List<Element> element = new List<Element>();
element.Add(2);
if(element.Contains(2))
Console.WriteLine("added");
else
Console.WriteLine("not added");
}
}
The Contains method does not use the == operator. What is the problem?
The Contains method does not use the == operator
No - it uses Equals, which you haven't overridden... so you're getting the default behaviour of Equals, which is to check for reference identity instead. You should override Equals(object) and GetHashCode to be consistent with each other - and for sanity's sake, consistent with your == overload too.
I'd also recommend implementing IEquatable<Element>, which List<Element> will use in preference to Equals(object), as EqualityComparer<T>.Default picks it up appropriately.
Oh, and your operator overloads should handle null references, too.
I'd also strongly recommend using private fields instead of public ones, and making your type immutable - seal it and make id readonly. Implementing equality for mutable types can lead to odd situations. For example:
Dictionary<Element, string> dictionary = new Dictionary<Element, string>();
Element x = new Element(10);
dictionary[x] = "foo";
x.id = 100;
Console.WriteLine(dictionary[x]); // No such element!
This would happen because the hash code would change (at least under most implementations), so the hash table underlying the dictionary wouldn't be able to find even a reference to the same object that's already in there.
So your class would look something like this:
internal sealed class Element : IEquatable<Element>
{
private readonly int id;
public int Id { get { return id; } }
public Element(int id)
{
this.id = id;
}
public static implicit operator Element(int d)
{
return new Element(d);
}
public static bool operator ==(Element e1, Element e2)
{
if (object.ReferenceEquals(e1, e2))
{
return true;
}
if (object.ReferenceEquals(e1, null) ||
object.ReferenceEquals(e2, null))
{
return false;
}
return e1.id == e2.id;
}
public static bool operator !=(Element e1, Element e2)
{
// Delegate...
return !(e1 == e2);
}
public bool Equals(Element other)
{
return this == other;
}
public override int GetHashCode()
{
return id;
}
public override bool Equals(object obj)
{
// Delegate...
return Equals(obj as Element);
}
}
(I'm not sure about the merit of the implicit conversion, by the way - I typically stay away from those, myself.)
The Contains method does not use the == operator. What is the problem?
That is correct.
This method [Contains] determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable.Equals method for T (the type of values in the list).
http://msdn.microsoft.com/en-us/library/bhkz42b3(v=vs.110).aspx
You need to override Equals() as well. Note when you overload Equals(), it is almost always correct to also override GetHashCode().
Override Equals and GetHashCode like:
class Element
{
public int id;
protected bool Equals(Element other)
{
return id == other.id;
}
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((Element) obj);
}
public override int GetHashCode()
{
return id; //or id.GetHashCode();
}
//..... rest of the class
See: List<T>.Contains Method
This method determines equality by using the default equality
comparer, as defined by the object's implementation of the
IEquatable<T>.Equals method for T (the type of values in the list).
I want to apply/practice DDD with my new project hence I am creating those typical DDD base classes, i.e., Entity, ValueObject, AggregateRoot and so on.
Question:
When you have the Entity base object implement IEquatable, should two entities with default value of the identity (Id) be considered as Not equal or equal?
For example, I use Guid type for the identity
public interface IEntity
{
Guid LocalId { get; }
}
public abstract class Entity : IEntity, IEquatable<Entity>
{
public Guid LocalId { get; private set; }
protected Entity()
{
this.LocalId = Guid.Empty;
}
protected Entity(Guid id)
{
if (Guid.Empty == id)
{
id = Guid.NewGuid();
}
this.LocalId = id;
}
public bool Equals(Entity other)
{
if (ReferenceEquals(other, null))
{
return false;
}
if (ReferenceEquals(other, this))
{
return true;
}
// **Question** - should I return false or true here?
if (other.LocalId == Guid.Empty && this.LocalId == Guid.Empty)
{
return false;
}
return (other.LocalId == this.LocalId);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(obj, null))
{
return false;
}
if (ReferenceEquals(obj, this))
{
return true;
}
if (!obj.GetType().Equals(typeof(Entity)))
{
return false;
}
return Equals((Entity)obj);
}
public override int GetHashCode()
{
return this.LocalId.GetHashCode();
}
public static bool operator==(Entity left, Entity right)
{
return Equals(left, right);
}
public static bool operator!=(Entity left, Entity right)
{
return !Equals(left, right);
}
}
If you want to follow the book on this then remember that:
An object that is not defined by its attributes, but rather by a
thread of continuity and its identity.
Now, in this context identity means "the property of objects that distinguishes them from other objects", which in most cases means the entity primary key (if your entities are persisted in a database) or some kind of Id attribute, in most cases a GUID will do the trick.
So to answer your question:
Yes, they're the same entity.
Be careful with how you create your
Id's
Why does this program print "not added" while I think it should print "added"?
using System;
using System.Collections.Generic;
class Element
{
public int id;
public Element(int id)
{
this.id = id;
}
public static implicit operator Element(int d)
{
Element ret = new Element(d);
return ret;
}
public static bool operator ==(Element e1, Element e2)
{
return (e1.id == e2.id);
}
public static bool operator !=(Element e1, Element e2)
{
return !(e1.id == e2.id);
}
}
class MainClass
{
public static void Main(string[] args)
{
List<Element> element = new List<Element>();
element.Add(2);
if(element.Contains(2))
Console.WriteLine("added");
else
Console.WriteLine("not added");
}
}
The Contains method does not use the == operator. What is the problem?
The Contains method does not use the == operator
No - it uses Equals, which you haven't overridden... so you're getting the default behaviour of Equals, which is to check for reference identity instead. You should override Equals(object) and GetHashCode to be consistent with each other - and for sanity's sake, consistent with your == overload too.
I'd also recommend implementing IEquatable<Element>, which List<Element> will use in preference to Equals(object), as EqualityComparer<T>.Default picks it up appropriately.
Oh, and your operator overloads should handle null references, too.
I'd also strongly recommend using private fields instead of public ones, and making your type immutable - seal it and make id readonly. Implementing equality for mutable types can lead to odd situations. For example:
Dictionary<Element, string> dictionary = new Dictionary<Element, string>();
Element x = new Element(10);
dictionary[x] = "foo";
x.id = 100;
Console.WriteLine(dictionary[x]); // No such element!
This would happen because the hash code would change (at least under most implementations), so the hash table underlying the dictionary wouldn't be able to find even a reference to the same object that's already in there.
So your class would look something like this:
internal sealed class Element : IEquatable<Element>
{
private readonly int id;
public int Id { get { return id; } }
public Element(int id)
{
this.id = id;
}
public static implicit operator Element(int d)
{
return new Element(d);
}
public static bool operator ==(Element e1, Element e2)
{
if (object.ReferenceEquals(e1, e2))
{
return true;
}
if (object.ReferenceEquals(e1, null) ||
object.ReferenceEquals(e2, null))
{
return false;
}
return e1.id == e2.id;
}
public static bool operator !=(Element e1, Element e2)
{
// Delegate...
return !(e1 == e2);
}
public bool Equals(Element other)
{
return this == other;
}
public override int GetHashCode()
{
return id;
}
public override bool Equals(object obj)
{
// Delegate...
return Equals(obj as Element);
}
}
(I'm not sure about the merit of the implicit conversion, by the way - I typically stay away from those, myself.)
The Contains method does not use the == operator. What is the problem?
That is correct.
This method [Contains] determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable.Equals method for T (the type of values in the list).
http://msdn.microsoft.com/en-us/library/bhkz42b3(v=vs.110).aspx
You need to override Equals() as well. Note when you overload Equals(), it is almost always correct to also override GetHashCode().
Override Equals and GetHashCode like:
class Element
{
public int id;
protected bool Equals(Element other)
{
return id == other.id;
}
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((Element) obj);
}
public override int GetHashCode()
{
return id; //or id.GetHashCode();
}
//..... rest of the class
See: List<T>.Contains Method
This method determines equality by using the default equality
comparer, as defined by the object's implementation of the
IEquatable<T>.Equals method for T (the type of values in the list).
This is a project question that i just cant seem to answer
using System;
namespace ConsoleApplication2
{
internal class Equipment : IComparable
{
private readonly string type;
private readonly int serialNo;
private string colour;
public decimal cost;
public Equipment(string type, int serialNo)
{
this.type = type == null ? "" : type.Trim();
this.serialNo = serialNo;
}
public string Key
{
get { return type + ":" + serialNo; }
}
int IComparable.CompareTo(object obj)
{
return 0;
}
}
}
(a) Override the appropriate method o ensure that different instances of the class that represent the same equipment item will be considered the same in the system.
(b) Override the appropriate method to enable instances of this class to be stored (and found) by key in a hash table
You should override the Equals and GetHashCode methods for this purpose.
Override Equals() with an appropriate logic of comparision
Override GetHashCode(), see GetHashCode Guidelines in C#
You must start reading this before doing such a task
Why is it important to override GetHashCode when Equals method is overriden in C#?
Writing GetHashCode manually is not that easy. Anyhow, that's code generated for this purpose by ReSharper. It's a complete solution. (It should be contained within your class definition of course). But what would you say, if you were asked - why and how it works? It might be embarassing.
So, apart from GetHashCode and Equals, which others have suggested you reading about, you might also look up http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx as well as http://msdn.microsoft.com/en-us/library/a569z7k8(v=VS.100).aspx
As for the mystery behind 397 in GetHashCode, have a look at this question here on StackOverflow: Why is '397' used for ReSharper GetHashCode override?
public bool Equals(Equipment other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(other.colour, colour) && other.cost == cost && other.serialNo == serialNo && Equals(other.type, type);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof (Equipment))
{
return false;
}
return Equals((Equipment) obj);
}
// note: if "Override the appropriate method to enable instances of this class
// to be stored (and found) by key in a hash table" is supposed to mean that only type and
// serialNo should be taken into account (since they are used to generate
// the Key value) - just remove the lines with cost and colour
public override int GetHashCode()
{
unchecked
{
int result = (colour != null ? colour.GetHashCode() : 0);
result = (result*397) ^ cost.GetHashCode();
result = (result*397) ^ serialNo;
result = (result*397) ^ (type != null ? type.GetHashCode() : 0);
return result;
}
}