这就是我在许多其他类继承的类中作为方法提出的.这个想法是它允许在相同类型的对象的属性之间进行简单比较.
现在,这确实有效 - 但为了提高我的代码质量,我想我会把它扔出去仔细检查.它怎么能更好/更有效/等等?
////// Compare property values (as strings) /// /// ///public bool PropertiesEqual(object comparisonObject) { Type sourceType = this.GetType(); Type destinationType = comparisonObject.GetType(); if (sourceType == destinationType) { PropertyInfo[] sourceProperties = sourceType.GetProperties(); foreach (PropertyInfo pi in sourceProperties) { if ((sourceType.GetProperty(pi.Name).GetValue(this, null) == null && destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null) == null)) { // if both are null, don't try to compare (throws exception) } else if (!(sourceType.GetProperty(pi.Name).GetValue(this, null).ToString() == destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null).ToString())) { // only need one property to be different to fail Equals. return false; } } } else { throw new ArgumentException("Comparison object must be of the same type.","comparisonObject"); } return true; }
Taras Alenin.. 160
我正在寻找一些代码片段,它们可以做类似于编写单元测试的帮助.这是我最终使用的.
public static bool PublicInstancePropertiesEqual(T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { Type type = typeof(T); List ignoreList = new List (ignore); foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) { if (!ignoreList.Contains(pi.Name)) { object selfValue = type.GetProperty(pi.Name).GetValue(self, null); object toValue = type.GetProperty(pi.Name).GetValue(to, null); if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))) { return false; } } } return true; } return self == to; }
编辑:
与上面相同的代码但使用LINQ和Extension方法:
public static bool PublicInstancePropertiesEqual(this T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { var type = typeof(T); var ignoreList = new List (ignore); var unequalProperties = from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance) where !ignoreList.Contains(pi.Name) && pi.GetUnderlyingType().IsSimpleType() && pi.GetIndexParameters().Length == 0 let selfValue = type.GetProperty(pi.Name).GetValue(self, null) let toValue = type.GetProperty(pi.Name).GetValue(to, null) where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)) select selfValue; return !unequalProperties.Any(); } return self == to; } public static class TypeExtensions { /// /// Determine whether a type is simple (String, Decimal, DateTime, etc) /// or complex (i.e. custom class with public properties and methods). /// ///public static bool IsSimpleType( this Type type) { return type.IsValueType || type.IsPrimitive || new[] { typeof(String), typeof(Decimal), typeof(DateTime), typeof(DateTimeOffset), typeof(TimeSpan), typeof(Guid) }.Contains(type) || (Convert.GetTypeCode(type) != TypeCode.Object); } public static Type GetUnderlyingType(this MemberInfo member) { switch (member.MemberType) { case MemberTypes.Event: return ((EventInfo)member).EventHandlerType; case MemberTypes.Field: return ((FieldInfo)member).FieldType; case MemberTypes.Method: return ((MethodInfo)member).ReturnType; case MemberTypes.Property: return ((PropertyInfo)member).PropertyType; default: throw new ArgumentException ( "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo" ); } } }
Liviu Trifoi.. 64
更新: Compare-Net-Objects的最新版本位于GitHub上 ,具有NuGet包和教程.它可以被称为
//This is the comparison class CompareLogic compareLogic = new CompareLogic(); ComparisonResult result = compareLogic.Compare(person1, person2); //These will be different, write out the differences if (!result.AreEqual) Console.WriteLine(result.DifferencesString);
或者,如果您需要更改某些配置,请使用
CompareLogic basicComparison = new CompareLogic() { Config = new ComparisonConfig() { MaxDifferences = propertyCount //add other configurations } };
可配置参数的完整列表在ComparisonConfig.cs中
原始答案:
我在您的代码中看到的限制:
最大的一点就是它没有进行深层对象比较.
如果属性是列表或包含列表作为元素,它不会逐元素进行元素比较(这可以是n级).
它没有考虑到不应该比较某些类型的属性(例如,用于过滤目的的Func属性,如PagedCollectionView类中的属性).
它不会跟踪实际上不同的属性(因此您可以在断言中显示).
我今天正在寻找一些单元测试的解决方案,通过属性深度比较来进行属性,最后我使用了:http://comparenetobjects.codeplex.com.
这是一个免费的图书馆,只有一个类,您可以像这样使用:
var compareObjects = new CompareObjects() { CompareChildren = true, //this turns deep compare one, otherwise it's shallow CompareFields = false, CompareReadOnly = true, ComparePrivateFields = false, ComparePrivateProperties = false, CompareProperties = true, MaxDifferences = 1, ElementsToIgnore = new List() { "Filter" } }; Assert.IsTrue( compareObjects.Compare(objectA, objectB), compareObjects.DifferencesString );
此外,它可以轻松地为Silverlight重新编译.只需将一个类复制到Silverlight项目中,然后删除一行或两行代码,以便进行Silverlight中不可用的比较,例如私有成员比较.
我正在寻找一些代码片段,它们可以做类似于编写单元测试的帮助.这是我最终使用的.
public static bool PublicInstancePropertiesEqual(T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { Type type = typeof(T); List ignoreList = new List (ignore); foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) { if (!ignoreList.Contains(pi.Name)) { object selfValue = type.GetProperty(pi.Name).GetValue(self, null); object toValue = type.GetProperty(pi.Name).GetValue(to, null); if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))) { return false; } } } return true; } return self == to; }
编辑:
与上面相同的代码但使用LINQ和Extension方法:
public static bool PublicInstancePropertiesEqual(this T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { var type = typeof(T); var ignoreList = new List (ignore); var unequalProperties = from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance) where !ignoreList.Contains(pi.Name) && pi.GetUnderlyingType().IsSimpleType() && pi.GetIndexParameters().Length == 0 let selfValue = type.GetProperty(pi.Name).GetValue(self, null) let toValue = type.GetProperty(pi.Name).GetValue(to, null) where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)) select selfValue; return !unequalProperties.Any(); } return self == to; } public static class TypeExtensions { /// /// Determine whether a type is simple (String, Decimal, DateTime, etc) /// or complex (i.e. custom class with public properties and methods). /// ///public static bool IsSimpleType( this Type type) { return type.IsValueType || type.IsPrimitive || new[] { typeof(String), typeof(Decimal), typeof(DateTime), typeof(DateTimeOffset), typeof(TimeSpan), typeof(Guid) }.Contains(type) || (Convert.GetTypeCode(type) != TypeCode.Object); } public static Type GetUnderlyingType(this MemberInfo member) { switch (member.MemberType) { case MemberTypes.Event: return ((EventInfo)member).EventHandlerType; case MemberTypes.Field: return ((FieldInfo)member).FieldType; case MemberTypes.Method: return ((MethodInfo)member).ReturnType; case MemberTypes.Property: return ((PropertyInfo)member).PropertyType; default: throw new ArgumentException ( "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo" ); } } }
更新: Compare-Net-Objects的最新版本位于GitHub上 ,具有NuGet包和教程.它可以被称为
//This is the comparison class CompareLogic compareLogic = new CompareLogic(); ComparisonResult result = compareLogic.Compare(person1, person2); //These will be different, write out the differences if (!result.AreEqual) Console.WriteLine(result.DifferencesString);
或者,如果您需要更改某些配置,请使用
CompareLogic basicComparison = new CompareLogic() { Config = new ComparisonConfig() { MaxDifferences = propertyCount //add other configurations } };
可配置参数的完整列表在ComparisonConfig.cs中
原始答案:
我在您的代码中看到的限制:
最大的一点就是它没有进行深层对象比较.
如果属性是列表或包含列表作为元素,它不会逐元素进行元素比较(这可以是n级).
它没有考虑到不应该比较某些类型的属性(例如,用于过滤目的的Func属性,如PagedCollectionView类中的属性).
它不会跟踪实际上不同的属性(因此您可以在断言中显示).
我今天正在寻找一些单元测试的解决方案,通过属性深度比较来进行属性,最后我使用了:http://comparenetobjects.codeplex.com.
这是一个免费的图书馆,只有一个类,您可以像这样使用:
var compareObjects = new CompareObjects() { CompareChildren = true, //this turns deep compare one, otherwise it's shallow CompareFields = false, CompareReadOnly = true, ComparePrivateFields = false, ComparePrivateProperties = false, CompareProperties = true, MaxDifferences = 1, ElementsToIgnore = new List() { "Filter" } }; Assert.IsTrue( compareObjects.Compare(objectA, objectB), compareObjects.DifferencesString );
此外,它可以轻松地为Silverlight重新编译.只需将一个类复制到Silverlight项目中,然后删除一行或两行代码,以便进行Silverlight中不可用的比较,例如私有成员比较.
我认为最好遵循Override Object#Equals()的模式以
获得更好的描述:阅读Bill Wagner的有效C# - 项目9我认为
public override Equals(object obOther) { if (null == obOther) return false; if (object.ReferenceEquals(this, obOther) return true; if (this.GetType() != obOther.GetType()) return false; # private method to compare members. return CompareMembers(this, obOther as ThisClass); }
同样在检查相等性的方法中,您应该返回true或false.要么它们是相同的,要么它们不是......而不是抛出异常,返回false.
我会考虑重写Object#Equals.
即使你必须考虑到这一点,使用反射来比较属性应该是缓慢的(我没有数字支持这一点).这是C#中valueType#Equals的默认行为,建议您对值类型重写Equals,并对性能进行成员比较.(之前我速读这个,因为你有自定义属性对象的集合......我的坏.)
2011年12月更新:
当然,如果类型已经有生产Equals(),那么你需要另一种方法.
如果您使用它来仅仅为了测试目的来比较不可变数据结构,那么您不应该将Equals添加到生产类(有人可能通过使用Equals实现来加密测试,或者您可能会阻止创建生产所需的Equals实现) .
如果性能无关紧要,您可以序列化它们并比较结果:
var serializer = new XmlSerializer(typeof(TheObjectType)); StringWriter serialized1 = new StringWriter(), serialized2 = new StringWriter(); serializer.Serialize(serialized1, obj1); serializer.Serialize(serialized2, obj2); bool areEqual = serialized1.ToString() == serialized2.ToString();
我认为Big T的答案相当不错,但缺乏深度比较,所以我稍微调整了一下:
using System.Collections.Generic; using System.Reflection; ///Comparison class. public static class Compare { ///Compare the public instance properties. Uses deep comparison. /// The reference object. /// The object to compare. /// Ignore property with name. ///Type of objects. ///public static bool PublicInstancePropertiesEqual True if both objects are equal, elsefalse .(T self, T to, params string[] ignore) where T : class { if (self != null && to != null) { var type = self.GetType(); var ignoreList = new List (ignore); foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (ignoreList.Contains(pi.Name)) { continue; } var selfValue = type.GetProperty(pi.Name).GetValue(self, null); var toValue = type.GetProperty(pi.Name).GetValue(to, null); if (pi.PropertyType.IsClass && !pi.PropertyType.Module.ScopeName.Equals("CommonLanguageRuntimeLibrary")) { // Check of "CommonLanguageRuntimeLibrary" is needed because string is also a class if (PublicInstancePropertiesEqual(selfValue, toValue, ignore)) { continue; } return false; } if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))) { return false; } } return true; } return self == to; } }