With C# 6.0 I can do this
var isEqual = x.Id == y.Id
&& x.UpdatedAt == y.UpdatedAt
&& x.Name == y.Name
&& x.RulesUrl == y.RulesUrl
&& x.OngoingChallenges?.Count == y.OngoingChallenges?.Count
&& x.MembershipIds?.Count == y.MembershipIds?.Count;
Is there any nice solution to do this with C# < 6.0?
I mean this part
&& x.OngoingChallenges?.Count == y.OngoingChallenges?.Count
&& x.MembershipIds?.Count == y.MembershipIds?.Count;
Because in old projects we do not have possibility to use C# 6.0. How to write isEqual
efficiently?
x.OnGoingChallenges?.Count
is equivalent to x.OnGoingChallenges != null ? x.OnGoingChallenges.Count : default(int?)
(there're other approaches, but at the end of the day is a shortcut to null checking called null-conditional operator).
That is, your code can't be rewritten with a syntatically elegant statement without C# 6, but you can emulate this new C# 6 feature using extension methods...
public static class StructExtensions
{
// Check that TProperty is nullable for the return value (this is how C#6's
// null-conditional operator works with value types
public static TProperty? GetOrDefault<TObject, TProperty>(this TObject someObject, Func<TObject, TProperty> propertySelectionFunc)
where TObject : class
where TProperty : struct
{
Contract.Requires(propertySelectionFunc != null);
return someObject == null ? default(TProperty?) : propertySelectionFunc(someObject);
}
}
And now your code in C#5 would look as follows:
var isEqual = x.Id == y.Id
&& x.UpdatedAt == y.UpdatedAt
&& x.Name == y.Name
&& x.RulesUrl == y.RulesUrl
&& x.OngoingChallenges.GetOrDefault(c => c.Count) == y.OngoingChallenges.GetOrDefault(c => c.Count)
&& x.MembershipIds.GetOrDefault(m => m.Count) == x.MembershipIds.GetOrDefault(m => m.Count);
The whole extension method would work for getting a value-typed property value or its default value. You might or might not extend the extension method class to also support getting a reference type value or null.
In C# version < 6.0 you would use ternary expressions
var isEqual = x.Id == y.Id
&& x.UpdatedAt == y.UpdatedAt
&& x.Name == y.Name
&& x.RulesUrl == y.RulesUrl
&& (x.OngoingChallenges == null ? 0 : x.OngoingChallenges.Count) ==
(y.OngoingChallenges == null ? 0 : y.OngoingChallenges.Count)
&& (x.MembershipIds == null : 0 ? x.MembershipIds.Count) ==
(y.MembershipIds == null : 0 : y.MembershipIds.Count);
As @Hamlet Hakobyan has pointed out, this not the semantically exact equivalent of the original C# 6.0 solution using ?.
, but you could change it to (according to @hvd):
int? count = x.MembershipIds == null : default(int?) ? x.MembershipIds.Count;
It depends whether you want to consider a missing collection and an empty collection as equal or not.
You could also use the null-coalescing operator ??
and provide a replacement object. Assuming that your objects are lists of some kind:
var empty = new List<int>();
var isEqual = x.Id == y.Id
&& x.UpdatedAt == y.UpdatedAt
&& x.Name == y.Name
&& x.RulesUrl == y.RulesUrl
&& (x.OngoingChallenges ?? empty).Count == (y.OngoingChallenges ?? empty).Count
&& (x.MembershipIds ?? empty).Count == (y.MembershipIds ?? empty).Count;
Before C# 6, i used something like this
public static class CommonExtensions
{
public static TValue TryGet<TObject, TValue>(this TObject obj, Func<TObject, TValue> getter, TValue defaultValue = default(TValue))
where TObject : class
{
return obj == null ? defaultValue : getter(obj);
}
//If objects types are equals
public static bool KeyEquals<TObject, TValue>(this TObject a, TObject b, Func<TObject, TValue> keyGetter)
where TObject : class
{
return a != null
&& b != null
&& EqualityComparer<TValue>.Default.Equals(keyGetter(a), keyGetter(b));
}
}
var isEqual = x.Id == y.Id
&& x.UpdatedAt == y.UpdatedAt
&& x.Name == y.Name
&& x.RulesUrl == y.RulesUrl
//v1
&& x.OngoingChallenges.TryGet(v => v.Count) == y.OngoingChallenges.TryGet(v => v.Count)
//v2
&& x.MembershipIds.KeyEquals(y.MembershipIds, v => v.Count);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With