How to emulate C# 6 null-conditional in C# < 6

本文关键字:lt in null-conditional to How emulate | 更新日期: 2023-09-27 18:28:07

使用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

How to emulate C# 6 null-conditional in C# < 6

x.OnGoingChallenges?.Count等效于x.OnGoingChallenges != null ? x.OnGoingChallenges.Count : default(int?)(还有其他方法,但最终有一种空检查的快捷方式,称为空条件运算符)。

也就是说,如果没有C#,您的代码就无法用语法优雅的语句重写,但您可以使用扩展方法来模拟这个新的C#特性。。。

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);

整个扩展方法将用于获取值类型的属性值或其默认值。您可能会也可能不会扩展扩展方法类以支持获取引用类型值或null。

在C#版本中<6.0您将使用三元表达式

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合并运算符??并提供一个替换对象。假设你的对象是某种列表:

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;

在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);