I'm trying to do a simple comparison on two objects, simply to track changes on saving to the database, I have the below code which works perfectly on simple objects.
public static List<Variance> Compare<T>(this T original, T current)
{
var variances = new List<Variance>();
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var v = new Variance
{
PropertyName = property.Name,
Original = property.GetValue(original),
Current = property.GetValue(current)
};
if (v.Original == null && v.Current == null)
{
continue;
}
if ((v.Original == null && v.Current != null) || (v.Original != null && v.Current == null))
{
variances.Add(v);
continue;
}
if (!v.Original.Equals(v.Current))
{
variances.Add(v);
}
}
return variances;
}
but if T is a list the equals need to be changed to a SequenceEqual and I can't think of a way to convert T to the correct list type to do the SequenceEqual check.
Although it's not impossible to use SequenceEqual here, it'd involve either dynamic like in Jasper Kent's answer, or quite a lot of reflection code, I'll outline what you'll need to do if you were using reflection:
check if both the objects are IEnumerable<T>.
get the Type of both objects, and then get the type parameter T as a Type as well
get the method SequenceEquals from Enumerable as a MethodInfo. This involves using LINQ to find the overload with two parameters.
call MakeGenericMethod with T.
Invoke the MethodInfo
I wouldn't want to read, or write that code... Using dynamic is fine I guess, though some people have different opinions...
So here's a third way: I suggest you write your own SequenceEqual method that takes IEnumerables (the non-generic version):
private static bool SequenceEqual(IEnumerable first, IEnumerable second) {
IEnumerator e1 = first.GetEnumerator();
IEnumerator e2 = second.GetEnumerator();
try {
// adapted from https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,63644a881e976b52,references
while (e1.MoveNext())
{
if (!(e2.MoveNext() && e1.Current.Equals(e2.Current))) return false;
}
if (e2.MoveNext()) return false;
return true;
} finally {
if (e1 is IDisposable d1) {
d1.Dispose();
}
if (e2 is IDisposable d2) {
d2.Dispose();
}
}
}
Then you can just check whether the objects are IEnumerable:
if (v.Original is Enumerable e1 &&
v.Current is Enumerable e2 &&
!SequenceEqual(e1, e2))
{
variances.Add(v);
}
I think the solution you want is to use dynamic overloading.
I've cut this solution down from yours to get to the nub of it. The call to SwitchEquals() is the equivalent of your v.Original.Equals(v.Current).
static bool SwitchEquals<T>(IEnumerable<T> listA, IEnumerable<T> listB)
{
Console.WriteLine("Doing Sequential Equals");
return true; // Do your sequential equal here
}
static bool SwitchEquals(object objA, object objB)
{
Console.WriteLine("Doing equals");
return objA.Equals(objB); // This is your original equals
}
static void Compare<T>(T original, T current)
{
// Using dynamic means the decision between the tow SwitchEquals methods is made
// At runtime, when the system knows if we have a collection
if (SwitchEquals ((dynamic)original,(dynamic) current))
Console.WriteLine("Match");
else
Console.WriteLine("No match");
}
static void Main(string[] args)
{
Compare(4,5);
Compare (new int[] { 4, 3 }, new int[] { 4, 4 });
}
Related
Given a passed-in IEnumerable, is there any built-in method to return a best-matching base type for all the items in the IEnumerable, that handles nullable/non-nullable types and inheritance?
Something like this:
var hs = new HashSet<object> {1,2,3,null};
Type t = GetSharedType(hs); //returns typeof(int?)
hs = new HashSet<object> {new BaseClass(), new DerivedClass()};
t = GetSharedType(hs); //returns typeof(BaseClass)
hs = new HashSet<object> {"1", 1};
t = GetSharedType(hs); //returns typeof(object) or null
(I am aware that I can write my own; my question is if there is something built-in).
No, there is no built-in mechanism to do this. You can combine several reflection APIs.
First you can retrieve real types for each object in collection with GetType():
IEnumerable<Type> realTypes = hs.Select(o => o.GetType());
Now you will have collection of Type class that have BaseType property and GetInterfaces() method. Ypu can use this code to get all hierarchy for each type:
public static IEnumerable<Type> GetParentTypes(this Type type)
{
// is there any base type?
if ((type == null) || (type.BaseType == null))
{
yield break;
}
// return all implemented or inherited interfaces
foreach (var i in type.GetInterfaces())
{
yield return i;
}
// return all inherited types
var currentBaseType = type.BaseType;
while (currentBaseType != null)
{
yield return currentBaseType;
currentBaseType= currentBaseType.BaseType;
}
}
You can use it to get collection of hierarchies:
IEnumerable<IEnumerable<Type>> baseTypes = realTypes.Select(t => t.GetParentTypes());
Next step is to merge all this list to have only intersected values. You can do it with Enumerable.Intersect method and this code:
public static IEnumerable<T> IntersectAllIfEmpty<T>(params IEnumerable<T>[] lists)
{
IEnumerable<T> results = null;
lists = lists.Where(l => l.Any()).ToArray();
if (lists.Length > 0)
{
results = lists[0];
for (int i = 1; i < lists.Length; i++)
results = results.Intersect(lists[i]);
}
else
{
results = new T[0];
}
return results;
}
Finally, we have:
IEnumerable<Type> realTypes = hs.Select(o => o.GetType());
IEnumerable<IEnumerable<Type>> baseTypes = realTypes.Select(t => t.GetParentTypes());
IEnumerable<Type> inersectedBaseTypes = IntersectAllIfEmpty(baseTypes);
Then we can use Type.IsAssignableFrom() method to iterate each type and ensure that one of them is assignable only from themself:
Type mostConcreteType = inersectedBaseTypes.Where(t => inersectedBaseTypes.Count(bt => t.IsAssignableFrom(bt)) == 1).FirstOrDefault();
No, there is no built in type for doing this. As you have identified by yourself that you have to write by your own. Something like
public static Type GetEnumerableType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
I have the following custom class deriving from Tuple:
public class CustomTuple : Tuple<List<string>, DateTime?>
{
public CustomTuple(IEnumerable<string> strings, DateTime? time)
: base(strings.OrderBy(x => x).ToList(), time)
{
}
}
and a HashSet<CustomTuple>. The problem is that when I add items to the set, they are not recognised as duplicates. i.e. this outputs 2, but it should output 1:
void Main()
{
HashSet<CustomTuple> set = new HashSet<CustomTuple>();
var a = new CustomTuple(new List<string>(), new DateTime?());
var b = new CustomTuple(new List<string>(), new DateTime?());
set.Add(a);
set.Add(b);
Console.Write(set.Count); // Outputs 2
}
How can I override the Equals and GetHashCode methods to cause this code to output a set count of 1?
You should override GetHashCode and Equals virtual methods defined in System.Object class.
Please remember that:
If two objects are logically "equal" then they MUST have the same hash code!
If two objects have the same hashcode, then it is not mandatory to have your objects equal.
Also, i've noticed an architectural problem in your code:
List is a mutable type but overriding Equals and GetHashCode usually makes your class logically to behave like a value type. So having "Item1" a mutable type and behaving like a value type is very dangerous. I suggest replacing your List with a ReadOnlyCollection . Then you would have to make a method that checks whether two ReadOnlyCollections are Equal.
For the GetHashCode () method, just compose a string from all string items found in Item1 then append a string that represents the Hash code for datetime then finally call on the concatenated result the "GetHashCode ()" overrided on string method. So normally you would have:
override int GetHashCode () {
return (GetHashCodeForList (Item1) + (Item2 ?? DateTime.MinValue).GetHashCode ()).GetHashCode ();
}
And the GetHashCodeForList method would be something like this:
private string GetHashCodeForList (IEnumerable <string> lst) {
if (lst == null) return string.Empty;
StringBuilder sb = new StringBuilder ();
foreach (var item in lst) {
sb.Append (item);
}
return sb.ToString ();
}
Final note: You could cache the GetHashCode result since it is relative expensive to get and your entire class would became immutable (if you replace List with a readonly collection).
A HashSet<T> will first call GetHashCode, so you need to work on that first. For an implementation, see this answer: https://stackoverflow.com/a/263416/1250301
So a simple, naive, implementation might look like this:
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + this.Item2.GetHashCode();
foreach (var s in this.Item1)
{
hash = hash * 23 + s.GetHashCode();
}
return hash;
}
}
However, if your lists are long, then this might not be efficient enough. So you'll have to decide where to compromise depending on how tolerant you are of collisions.
If the result of GetHashCode for two items are the same, then, and only then, will it call Equals. An implementation of Equals is going to need to compare the items in the list. Something like this:
public override bool Equals(object o1)
{
var o = o1 as CustomTuple;
if (o == null)
{
return false;
}
if (Item2 != o.Item2)
{
return false;
}
if (Item1.Count() != o.Item1.Count())
{
return false;
}
for (int i=0; i < Item1.Count(); i++)
{
if (Item1[i] != o.Item1[i])
{
return false;
}
}
return true;
}
Note that we check the date (Item2) first, because that's cheap. If the date isn't the same, we don't bother with anything else. Next we check the Count on both collections (Item1). If they don't match, there's no point iterating the collections. Then we loop through both collections and compare each item. Once we find one that doesn't match, we return false because there is no point continuing to look.
As pointed out in George's answer, you also have the problem that your list is mutable, which will cause problems with your HashSet, for example:
var a = new CustomTuple(new List<string>() {"foo"} , new DateTime?());
var b = new CustomTuple(new List<string>(), new DateTime?());
set.Add(a);
set.Add(b);
// Hashset now has two entries
((List<string>)a.Item1).Add("foo");
// Hashset still has two entries, but they are now identical.
To solve that, you need to force your IEnumerable<string> to be readonly. You could do something like:
public class CustomTuple : Tuple<IReadOnlyList<string>, DateTime?>
{
public CustomTuple(IEnumerable<string> strings, DateTime? time)
: base(strings.OrderBy(x => x).ToList().AsReadOnly(), time)
{
}
public override bool Equals(object o1)
{
// as above
}
public override int GetHashCode()
{
// as above
}
}
This is is what I went for, which outputs 1 as desired:
private class CustomTuple : Tuple<List<string>, DateTime?>
{
public CustomTuple(IEnumerable<string> strings, DateTime? time)
: base(strings.OrderBy(x => x).ToList(), time)
{
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var that = (CustomTuple) obj;
if (Item1 == null && that.Item1 != null || Item1 != null && that.Item1 == null) return false;
if (Item2 == null && that.Item2 != null || Item2 != null && that.Item2 == null) return false;
if (!Item2.Equals(that.Item2)) return false;
if (that.Item1.Count != Item1.Count) return false;
for (int i = 0; i < Item1.Count; i++)
{
if (!Item1[i].Equals(that.Item1[i])) return false;
}
return true;
}
public override int GetHashCode()
{
int hash = 17;
hash = hash*23 + Item2.GetHashCode();
return Item1.Aggregate(hash, (current, s) => current*23 + s.GetHashCode());
}
}
Let's say I have an object which may be of type IEnumerable<T>. I want to write a method that returns true if the object is of type IEnumerable<T>, is not null, and is not empty.
Here's what I've got so far:
public bool IsNullOrEmpty(object obj)
{
if (obj != null)
{
if (obj is IEnumerable<object>)
{
return (obj as IEnumerable<object>).Any();
}
}
return false;
}
This works if I pass in an object that is of type List<string>, but not if I pass in an object that is of type List<int>. It fails because because obj is IEnumerable<object> returns false.
Any idea how I can make this work for all generic IEnumerables?
Since the type may be unknown, you can try check for IEnumerable interface and use MoveNext() on the enumerator.
EDIT: I updated the method name. It makes more sense with the logic now since the original question code was checking if there were items in the collection.
public bool IsNotNullOrEmpty(object enumerable)
{
if (enumerable != null)
{
if (enumerable is IEnumerable)
{
using(var enumerator = ((IEnumerable)enumerable).GetEnumerator())
return enumerator.MoveNext();
}
}
return false;
}
System.Collections.Generic.IEnumerable<T> inherits from System.Collections.IEnumerable - thus, if you are ok with checking the non-generic IEnumerable, rather than the generic IEnumerable<T>, you could just cast to IEnumerable.
A few notes about your code: You are first checking with is, and then you cast with as. That is generally unnecessary; as already checks and returns null if the cast failed. Therefore, a shorter way would be:
var enumerable = obj as IEnumerable;
if (enumerable != null) {
return !enumerable.Cast<object>().Any();
}
Note that you will need the additional call to Cast there, as Any requires a generic IEnumerable<T>.
You can try to cast it to IEnumerable:
public static bool IsNullOrEmpty<T>(this T obj) where T : class
{
if (obj == null) return true;
IEnumerable seq = obj as IEnumerable;
if (seq != null) return !seq.Cast<object>().Any();
return false;
}
...
List<int> list = new List<int>();
bool nullOrEmpty = list.IsNullOrEmpty(); // true
Btw, interestingly enough it works correctly with an empty string:
bool nullOrEmpty = "".IsNullOrEmpty(); // true
You can check for the non-generic IEnumerable and check that for emptiness. You can add a check to ensure the object implements IEnumerable<T> using reflection:
public static bool IsNullOrEmpty(object obj)
{
var e = obj as System.Collections.IEnumerable;
if (e == null || !e.GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))) return false;
foreach (object _ in e)
{
return true;
}
return false;
}
For completeness, adding the state-of-the-art answers for the recent c# versions (from c#8.0):
static bool IsNotNullOrEmpty<T>(object obj) => obj is IEnumerable<T> seq && seq.Any();
static bool IsNullOrEmpty<T>(object obj) => !(obj is IEnumerable<T> seq && seq.Any());
This assumes you will know T in the calling site.
If you won't know T, you shouldn't be using IEnumerable<object>, but IEnumerable instead. Something like this:
static bool IsNotNullOrEmpty(object obj) => obj is IEnumerable seq && seq.GetEnumerator().MoveNext();
You can first check if the object implements ICollection like lists and arrays do, as checking the size of one of those is cheaper as they have a Count property. If it's not you can check if it implements IEnumerable and if it does, create an enumerator and see if you can move to the first item:
public static bool IsNullOrEmpty(object obj) {
ICollection c = obj as ICollection;
if (c != null) {
return c.Count == 0;
}
IEnumerable e = obj as IEnumerable;
if (e != null) {
return !e.GetEnumerator().MoveNext();
}
return false;
}
This answers expands the question outside of the IEnumerable<T> scope to "can this thing I have be used in a foreach-loop?".
As you might known, foreach does not check for IEnumerable primarily, but rather tries to enumerate whatever is given.
This will work for all types which in some sense is enumerable in the sense that foreach thinks:
public bool IsNullOrEmpty(object obj)
{
if (obj != null) // we can't be null
{
var method = obj.GetType().GetMethod("GetEnumerator");
if (method != null) // we have an enumerator method
{
var enumerator = method.Invoke(obj, null);
if (enumerator != null) // we got the enumerator
{
var moveMethod = enumerator.GetType().GetMethod("MoveNext")
if (moveMethod != null) // we have a movenext method, lets try it
return !(bool)moveMethod.Invoke(enumerator, null); // ie true = empty, false = has elements
}
}
}
return true; // it's empty, lets return true
}
This basically does the same as the foreach-loop, ie checks for enumerability and doesn't really give a darn about the types involved.
As such it should really be avoided, but if you somehow have to check if you can put your type in foreach-loop this should do the trick.
Following on from my question here, I'm trying to create a generic value equality comparer. I've never played with reflection before so not sure if I'm on the right track, but anyway I've got this idea so far:
bool ContainSameValues<T>(T t1, T t2)
{
if (t1 is ValueType || t1 is string)
{
return t1.Equals(t2);
}
else
{
IEnumerable<PropertyInfo> properties = t1.GetType().GetProperties().Where(p => p.CanRead);
foreach (var property in properties)
{
var p1 = property.GetValue(t1, null);
var p2 = property.GetValue(t2, null);
if( !ContainSameValues<p1.GetType()>(p1, p2) )
return false;
}
}
return true;
}
This doesn't compile because I can't work out how to set the type of T in the recursive call. Is it possible to do this dynamically at all?
There are a couple of related questions on here which I have read but I couldn't follow them enough to work out how they might apply in my situation.
You can avoid reflection on invocation if you are happy to compare based on the statically know types of the properties.
This relies on Expressions in 3.5 to do the one off reflection in a simple manner, it is possible to do this better to reduce effort for extremely nested types but this should be fine for most needs.
If you must work off the runtime types some level of reflection will be required (though this would be cheap if you again cache the per property access and comparison methods) but this is inherently much more complex since the runtime types on sub properties may not match so, for full generality you would have to consider rules like the following:
consider mismatched types to NOT be equal
simple to understand and easy to implement
not likely to be a useful operation
At the point the types diverge use the standard EqualityComparer<T>.Default implementation on the two and recurse no further
again simple, somewhat harder to implement.
consider equal if they have a common subset of properties which are themselves equal
complicated, not really terribly meaningful
consider equal if they share the same subset of properties (based on name and type) which are themselves equal
complicated, heading into Duck Typing
There are a variety of other options but this should be food for thought as to why full runtime analysis is hard.
(note that I have changed you 'leaf' termination guard to be what I consider to be superior, if you want to just use sting/value type for some reason feel free)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;
class StaticPropertyTypeRecursiveEquality<T>
{
private static readonly Func<T,T, bool> actualEquals;
static StaticPropertyTypeRecursiveEquality()
{
if (typeof(IEquatable<T>).IsAssignableFrom(typeof(T)) ||
typeof(T).IsValueType ||
typeof(T).Equals(typeof(object)))
{
actualEquals =
(t1,t2) => EqualityComparer<T>.Default.Equals(t1, t2);
}
else
{
List<Func<T,T,bool>> recursionList = new List<Func<T,T,bool>>();
var getterGeneric =
typeof(StaticPropertyTypeRecursiveEquality<T>)
.GetMethod("MakePropertyGetter",
BindingFlags.NonPublic | BindingFlags.Static);
IEnumerable<PropertyInfo> properties = typeof(T)
.GetProperties()
.Where(p => p.CanRead);
foreach (var property in properties)
{
var specific = getterGeneric
.MakeGenericMethod(property.PropertyType);
var parameter = Expression.Parameter(typeof(T), "t");
var getterExpression = Expression.Lambda(
Expression.MakeMemberAccess(parameter, property),
parameter);
recursionList.Add((Func<T,T,bool>)specific.Invoke(
null,
new object[] { getterExpression }));
}
actualEquals = (t1,t2) =>
{
foreach (var p in recursionList)
{
if (t1 == null && t2 == null)
return true;
if (t1 == null || t2 == null)
return false;
if (!p(t1,t2))
return false;
}
return true;
};
}
}
private static Func<T,T,bool> MakePropertyGetter<TProperty>(
Expression<Func<T,TProperty>> getValueExpression)
{
var getValue = getValueExpression.Compile();
return (t1,t2) =>
{
return StaticPropertyTypeRecursiveEquality<TProperty>
.Equals(getValue(t1), getValue(t2));
};
}
public static bool Equals(T t1, T t2)
{
return actualEquals(t1,t2);
}
}
for testing I used the following:
public class Foo
{
public int A { get; set; }
public int B { get; set; }
}
public class Loop
{
public int A { get; set; }
public Loop B { get; set; }
}
public class Test
{
static void Main(string[] args)
{
Console.WriteLine(StaticPropertyTypeRecursiveEquality<String>.Equals(
"foo", "bar"));
Console.WriteLine(StaticPropertyTypeRecursiveEquality<Foo>.Equals(
new Foo() { A = 1, B = 2 },
new Foo() { A = 1, B = 2 }));
Console.WriteLine(StaticPropertyTypeRecursiveEquality<Loop>.Equals(
new Loop() { A = 1, B = new Loop() { A = 3 } },
new Loop() { A = 1, B = new Loop() { A = 3 } }));
Console.ReadLine();
}
}
You need to call the method using reflection, like this:
MethodInfo genericMethod = typeof(SomeClass).GetMethod("ContainSameValues");
MethodInfo specificMethod = genericMethod.MakeGenericMethod(p1.GetType());
if (!(bool)specificMethod.Invoke(this, new object[] { p1, p2 }))
However, your method should not be generic in the first place; it should simply take two object parameters. (Or, if it is generic, it should cache properties and delegates in a generic type)
How does the == operator really function in C#? If it used to compare objects of class A, will it try to match all of A's properties, or will it look for pointers to the same memory location (or maybe something else)?
Let's create a hypothetical example. I'm writing an application that utilizes the Twitter API, and it has a Tweet class, which has all the properties of a single tweet: text, sender, date&time, source, etc. If I want to compare objects of class Tweet for equivalence, can I just use:
Tweet a, b;
if (a == b)
{
//do something...
}
Will that check for equivalence of all the properties of the Tweet class between a and b?
If not, would the correct approach be to overload the == operator to explicitly check for equivalence of all the fields?
UPDATE: From the first two answers, am I right in assuming:
If the == operator or Equals method is not overloaded for a class, the == operator for the object class is used.
The == operator for the object class checks for equality in memory location.
I have to overload the == operator or the Equals method to accomplish this task.
In the overload, I have to check for equivalence in properties manually, so there is no way to do it semi-automatically, say, in a loop, right?
UPDATE #2: Yuriy made a comment that it is possible to do check for equivalence in properties in the == operator with reflection. How can this be done? Could you give me some sample code? Thanks!
For reference types, the default implementations of both the == operator and the Equals() method will simply check that both objects have the same reference, and are therefore the same instance.
If you want to check the contents of two different objects are equal then you must write the code to do it yourself, one way or another. It would be possible to do with reflection (the MbUnit test framework does something along these lines) but with a heavy performance penalty and a good chance that it wouldn't do quite what you expected anyway, and you should implement == or Equals and GetHashCode by hand.
MSDN has a good example of how to do it:
public override bool Equals(object o)
{
try
{
return (bool) (this == (DBBool) o);
}
catch
{
return false;
}
}
Then you overload the == and !=:
// Equality operator. Returns dbNull if either operand is dbNull,
// otherwise returns dbTrue or dbFalse:
public static DBBool operator ==(DBBool x, DBBool y)
{
if (x.value == 0 || y.value == 0) return dbNull;
return x.value == y.value? dbTrue: dbFalse;
}
// Inequality operator. Returns dbNull if either operand is
// dbNull, otherwise returns dbTrue or dbFalse:
public static DBBool operator !=(DBBool x, DBBool y)
{
if (x.value == 0 || y.value == 0) return dbNull;
return x.value != y.value? dbTrue: dbFalse;
}
And don't forget to overload the GetHash method.
Edit:
I wrote the following quick sample for using reflection in a compare. This would have to be much more comprehensive, I might try doing a blog on it if people want me to:
public class TestEquals
{
private int _x;
public TestEquals(int x)
{
this._x = x;
}
public override bool Equals(object obj)
{
TestEquals te = (TestEquals)obj;
if (te == null) return false;
foreach (var field in typeof(TestEquals)
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
if (!field.GetValue(this).Equals(field.GetValue(te)))
return false;
}
return true;
}
}
The proper approach is the overload the equals method of the Tweet class in addition to the == operator, as described here.
Will that check for equivalence of all the properties of the Tweet class between a and b?
No
If not, would the correct approach be to overload the == operator to explicitly check for equivalence of all the fields?
You can either overload the == operator, or overload the Equals function.
Edit
#Yuriy gave a good example for compating all the non public variables. Since i also wrote an example, here it is (mine compares properties)
class TwitterItem
{
private string myValue = "default value";
public string Value1
{
get { return myValue; }
set { myValue = value; }
}
public string Value2
{
get { return myValue; }
set { myValue = value; }
}
public string Value3
{
get { return myValue; }
set { myValue = value; }
}
public override bool Equals(object obj)
{
if (base.Equals(obj)) return true;
Type type = typeof(TwitterItem);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (false == property.GetValue(this, null).Equals(property.GetValue(obj, null)))
return false;
}
return true;
}
}
You can compare the properties using reflection:
var a = new Entity() { Name = "test", ID = "1" };
var b = new Entity() { Name = "test", ID = "1" };
var c = new Entity() { Name = "test", ID = "2" };
System.Diagnostics.Debug.WriteLine(a.Equals(b));//Returns true
System.Diagnostics.Debug.WriteLine(a.Equals(c));//Returns false
public class Entity
{
public string Name { get; set; }
public string ID { get; set; }
public override bool Equals(object obj)
{
var t = obj.GetType();
foreach (var p in t.GetProperties())
{
if (t.GetProperty(p.Name).GetValue(obj, null) != t.GetProperty(p.Name).GetValue(this, null))
return false;
}
return true;
}
}