如何在 C# < 6 中模拟 C# 6 空条件
How to emulate C# 6 null-conditional in C# < 6
使用 C# 6.0 我可以做到这一点
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;
C# < 6.0 有什么好的解决方案吗?
我是说这部分
&& x.OngoingChallenges?.Count == y.OngoingChallenges?.Count
&& x.MembershipIds?.Count == y.MembershipIds?.Count;
因为在旧项目中我们没有可能使用 C# 6.0。如何高效地写isEqual
?
在 C# 版本 < 6.0 中,您将使用 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);
正如@Hamlet Hakobyan 所指出的,这不是使用 ?.
的原始 C# 6.0 解决方案在语义上完全等效,但您可以将其更改为(根据@hvd):
int? count = x.MembershipIds == null : default(int?) ? x.MembershipIds.Count;
这取决于您是否要将缺失集合和空集合视为相等。
您也可以使用 null-coalescing operator ??
并提供替换对象。假设您的对象是某种列表:
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;
x.OnGoingChallenges?.Count
等同于 x.OnGoingChallenges != null ? x.OnGoingChallenges.Count : default(int?)
(还有其他方法,但归根结底,空值检查的快捷方式称为 null-conditional operator).
也就是说,如果没有 C# 6,您的代码将无法使用 语法优雅的语句 重写,但是您可以使用扩展方法来模拟这个新的 C# 6 功能...
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);
}
}
现在您在 C#5 中的代码如下所示:
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);
整个扩展方法适用于获取值类型 属性 值或其默认值。您可能会也可能不会扩展扩展方法 class 以支持获取引用类型值或 null。
在 C# 6 之前,我使用了类似这样的东西
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);
使用 C# 6.0 我可以做到这一点
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;
C# < 6.0 有什么好的解决方案吗?
我是说这部分
&& x.OngoingChallenges?.Count == y.OngoingChallenges?.Count
&& x.MembershipIds?.Count == y.MembershipIds?.Count;
因为在旧项目中我们没有可能使用 C# 6.0。如何高效地写isEqual
?
在 C# 版本 < 6.0 中,您将使用 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);
正如@Hamlet Hakobyan 所指出的,这不是使用 ?.
的原始 C# 6.0 解决方案在语义上完全等效,但您可以将其更改为(根据@hvd):
int? count = x.MembershipIds == null : default(int?) ? x.MembershipIds.Count;
这取决于您是否要将缺失集合和空集合视为相等。
您也可以使用 null-coalescing operator ??
并提供替换对象。假设您的对象是某种列表:
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;
x.OnGoingChallenges?.Count
等同于 x.OnGoingChallenges != null ? x.OnGoingChallenges.Count : default(int?)
(还有其他方法,但归根结底,空值检查的快捷方式称为 null-conditional operator).
也就是说,如果没有 C# 6,您的代码将无法使用 语法优雅的语句 重写,但是您可以使用扩展方法来模拟这个新的 C# 6 功能...
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);
}
}
现在您在 C#5 中的代码如下所示:
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);
整个扩展方法适用于获取值类型 属性 值或其默认值。您可能会也可能不会扩展扩展方法 class 以支持获取引用类型值或 null。
在 C# 6 之前,我使用了类似这样的东西
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);