如何在C#中模拟C#6空条件<;6.
用C#6.0,我可以做到这一点如何在C#中模拟C#6空条件<;6.,c#,c#-5.0,c#-6.0,C#,C# 5.0,C# 6.0,用C#6.0,我可以做到这一点 var isEqual = x.Id == y.Id && x.UpdatedAt == y.UpdatedAt && x.Name == y.Name && x.RulesUrl == y.RulesUrl && x.OngoingChall
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#version<6.0中 正如@Hamlet Hakobyan所指出的,这在语义上并不完全等同于使用
?。
的原始C#6.0解决方案,但您可以将其更改为(根据@hvd):
这取决于您是否想考虑一个缺失的集合和一个空集合是否相等。
您还可以使用并提供替换对象。假设对象是某种类型的列表:
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;
var empty=新列表();
变量isEqual=x.Id==y.Id
&&x.UpdatedAt==y.UpdatedAt
&&x.Name==y.Name
&&x.RulesUrl==y.RulesUrl
&&(x.OngoingChallenges??空)。计数==(y.OngoingChallenges??空)。计数
&&(x.MembershipID??空)。计数==(y.MembershipID??空)。计数;
x.OnGoingChallenges?.Count
相当于x.OnGoingChallenges!=无效的x、 Count:default(int?
(还有其他方法,但最后是一种称为空条件运算符的空检查快捷方式)
也就是说,在没有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);
}
}
整个扩展方法将用于获取值类型的属性值或其默认值。您可能会也可能不会扩展扩展方法类来支持获取引用类型值或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);
公共静态类公共扩展
{
公共静态TValue TryGet(这个TObject对象,Func getter,TValue defaultValue=default(TValue))
对象:班级
{
返回obj==null?默认值:getter(obj);
}
//如果对象类型等于
公共静态bool KeyEquals(此TObject a、TObject b、Func keyGetter)
对象:班级
{
返回一个!=null
&&b!=null
&&EqualityComparer.Default.Equals(keyGetter(a),keyGetter(b));
}
}
变量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.membershipId.KeyEquals(y.membershipId,v=>v.Count);
那么您的解决方案是什么?@senzacionale,例如三元运算符:(x.MembershipId==null?(int?)null:x.MembershipId.Count)
“因为在旧项目中,我们不可能使用C#6.0”。为什么不呢?如果你有Visual Studio 2015,你可以针对较旧的.NET运行时,但仍然使用新的C#6编译器和语法。编译后的IL代码与更详细的旧语法没有区别。您的第一个解决方案存在问题。它假设null等于0
,这是错误的假设。如果每次读取属性时都构造一个新列表(或返回任何类型),那么在性能方面,两次读取OngoingChallenges
属性可能是一个问题。如果它是一个只读列表,但它的目标是一个没有ReadOnlyCollection
的框架版本,那么这可能是它每次都会返回一个新列表的一个很好的原因。第二个选项为空
,很好地避免了这个问题。(吹毛求疵:你不会把a==b
称为“你会使用一个二进制表达式”,你会直截了当地提到等式运算符。?:
被称为条件运算符。)@HamletHakobyan的观点也是公平的。通过将0
更改为default(int?
/(int?)null
可以很容易地解决这个问题。我认为使用default(int?
或(int?)null
的解决方案与原始C#6.0解决方案最匹配。但根据情况,将缺少的集合和空集合视为相等仍然可以接受。?:
不要求第二个和第三个操作数具有相同的类型。它只需要一种类型可以转换为另一种类型<代码>x.MembershipId==null:null?x、 MembershipIds.Count不会编译,因为null
没有int
可以转换的类型,并且null
不能转换为int
。但是,x.membershipId==null:default(int?)?x、 MembershipId.Count将被编译,因为第二个操作数的类型为int?
,而类型为int
的第三个操作数可以隐式转换为int?
。你对我的评论是哈姆雷特·哈科比安的。:)someObject.Equals(默认值(TProperty))
看起来不正确。除了抛出NullReferenceException
之外,如果someObject
是null
,您应该将其与default(TObject)
进行比较,而不是default(TProperty)
。由于您需要TObject:class
,您知道默认值(TObject)
为null
,在这一点上,您可以简单地编写someObject==null
。这是一个很好的主意,但我有一个错误:不包含“OngoingChallenges”的定义…@senzacionale除了GetOrDefault
定义本身是错误的之外,示例用法也有问题。它应该是x.OngoingChallenges.GetOrDefault(c=>c.Count)
,而不是x.OngoingChallenges.GetOrDefault(c=>c.OngoingChallenges.Count)
。其他用途相同。:)这应该可以解释你看到的错误。@hvd是的,你在所有问题上都是对的。我已经修好了,谢谢。我正在改进样本,但忘记了r
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);
}
}
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);
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);