Das habe ich mir als Methode für eine Klasse ausgedacht, die von vielen meiner anderen Klassen geerbt wurde. Die Idee ist, dass es den einfachen Vergleich zwischen Eigenschaften von Objekten desselben Typs ermöglicht.
Nun, das funktioniert - aber im Interesse der Verbesserung der Qualität meines Codes dachte ich, ich würde es zur Überprüfung rauswerfen. Wie kann es besser/effizienter/etc. Sein?
/// <summary>
/// Compare property values (as strings)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
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;
}
Ich war auf der Suche nach einem Codeausschnitt, der beim Schreiben von Komponententests eine ähnliche Funktion hat. Folgendes habe ich letztendlich verwendet.
public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(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;
}
EDIT:
Gleicher Code wie oben, jedoch mit den Methoden LINQ und Extension:
public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = typeof(T);
var ignoreList = new List<string>(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
{
/// <summary>
/// Determine whether a type is simple (String, Decimal, DateTime, etc)
/// or complex (i.e. custom class with public properties and methods).
/// </summary>
/// <see cref="http://stackoverflow.com/questions/2442534/how-to-test-if-type-is-primitive"/>
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"
);
}
}
}
UPDATE: Die neueste Version von Compare-Net-Objects befindet sich auf GitHub , hat NuGet-Paket = und Tutorial . Man kann es so nennen
//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);
Oder wenn Sie eine Konfiguration ändern müssen, verwenden Sie
CompareLogic basicComparison = new CompareLogic()
{ Config = new ComparisonConfig()
{ MaxDifferences = propertyCount
//add other configurations
}
};
Die vollständige Liste der konfigurierbaren Parameter finden Sie in ComparisonConfig.cs
Ursprüngliche Antwort:
Die Einschränkungen, die ich in Ihrem Code sehe:
Das Größte ist, dass es keinen tiefen Objektvergleich macht.
Es wird kein Element-für-Element-Vergleich durchgeführt, wenn Eigenschaften Listen sind oder Listen als Elemente enthalten (dies kann n-stufig sein).
Es wird nicht berücksichtigt, dass einige Eigenschaften nicht verglichen werden sollten (z. B. eine Func-Eigenschaft, die zu Filterzwecken verwendet wird, wie die in der PagedCollectionView-Klasse).
Es wird nicht nachverfolgt, welche Eigenschaften tatsächlich unterschiedlich waren (sodass Sie dies in Ihren Behauptungen anzeigen können).
Ich habe heute nach einer Lösung für Unit-Testzwecke gesucht, um einen umfassenden Vergleich von Eigenschaften zu erstellen, und habe am Ende Folgendes verwendet: http://comparenetobjects.codeplex.com .
Es ist eine kostenlose Bibliothek mit nur einer Klasse, die Sie einfach so verwenden können:
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<string>() { "Filter" }
};
Assert.IsTrue(
compareObjects.Compare(objectA, objectB),
compareObjects.DifferencesString
);
Es kann auch leicht für Silverlight neu kompiliert werden. Kopieren Sie einfach die eine Klasse in ein Silverlight-Projekt und entfernen Sie ein oder zwei Codezeilen für Vergleiche, die in Silverlight nicht verfügbar sind, wie z. B. der Vergleich privater Mitglieder.
Wenn die Leistung keine Rolle spielt, können Sie sie serialisieren und die Ergebnisse vergleichen:
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();
Ich denke, es wäre am besten, dem Muster für Override Object # Equals () zu folgen.
Zur besseren Beschreibung: Lies Bill Wagners Wirksam C # - Punkt 9, denke ich
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);
}
Update Dezember 2011:
Ich denke, die Antwort von Big T war ziemlich gut, aber der tiefe Vergleich fehlte, also habe ich es ein wenig optimiert:
using System.Collections.Generic;
using System.Reflection;
/// <summary>Comparison class.</summary>
public static class Compare
{
/// <summary>Compare the public instance properties. Uses deep comparison.</summary>
/// <param name="self">The reference object.</param>
/// <param name="to">The object to compare.</param>
/// <param name="ignore">Ignore property with name.</param>
/// <typeparam name="T">Type of objects.</typeparam>
/// <returns><see cref="bool">True</see> if both objects are equal, else <see cref="bool">false</see>.</returns>
public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = self.GetType();
var ignoreList = new List<string>(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;
}
}
Ich würde der PublicInstancePropertiesEqual-Methode die folgende Zeile hinzufügen, um Fehler beim Kopieren und Einfügen zu vermeiden:
Assert.AreNotSame(self, to);
Überschreiben Sie .ToString () für alle Objekte in den Eigenschaften? Andernfalls könnte dieser zweite Vergleich mit null zurückkommen.
Auch in diesem zweiten Vergleich bin ich auf dem Zaun, was das Konstrukt von! (A == B) im Vergleich zu (A! = B) betrifft, was die Lesbarkeit in sechs Monaten/zwei Jahren betrifft. Die Linie selbst ist ziemlich breit, was in Ordnung ist, wenn Sie einen breiten Monitor haben, aber möglicherweise nicht sehr gut drucken. (Nitpick)
Verwenden alle Ihre Objekte immer Eigenschaften, sodass dieser Code funktioniert? Könnte es einige interne Daten ohne Eigenschaften geben, die sich von Objekt zu Objekt unterscheiden könnten, aber alle offen gelegten Daten sind gleich? Ich denke an einige Daten, die sich im Laufe der Zeit ändern könnten, wie zwei Zufallszahlengeneratoren, die zufällig an einem Punkt die gleiche Zahl treffen, aber zwei verschiedene Informationssequenzen erzeugen, oder einfach Daten, die nicht offengelegt werden über die Eigenschaftsschnittstelle.
Der Vollständigkeit halber möchte ich noch einen Verweis hinzufügen auf http: //www.cyotek.com/blog/comparing-the-properties-of-two-objects-via-reflection Es hat eine vollständigere Logik als Die meisten anderen antworten auf dieser Seite.
Ich bevorzuge jedoch Compare-Net-Objects library https: //github.com/GregFinzer/Compare-Net-Objects (verwiesen von Liviu Trifoi 's = antworte )
Die Bibliothek verfügt über das NuGet-Paket http: //www.nuget.org/packages/CompareNETObjects und mehrere zu konfigurierende Optionen.
Wenn Sie nur Objekte desselben Typs oder weiter unten in der Vererbungskette vergleichen, geben Sie den Parameter nicht als Basistyp, sondern als Objekt an.
Überprüfen Sie auch den Parameter auf Null.
Außerdem würde ich 'var' benutzen, um den Code besser lesbar zu machen (wenn es sich um C # 3 handelt).
Wenn das Objekt über Referenztypen als Eigenschaften verfügt, rufen Sie lediglich ToString () auf, wodurch die Werte nicht wirklich verglichen werden. Wenn ToString nicht überschrieben wird, wird nur der Typname als String zurückgegeben, der falsch-positive Ergebnisse liefern kann.
Am Ende tat ich Folgendes:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b)
{
int count = a.GetType().GetProperties().Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe();
bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb)
{
return false;
}
}
return true;
}
Verwendung:
if (Compare<ObjectType>(a, b))
pdate
Wenn Sie einige Eigenschaften nach Namen ignorieren möchten:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b, params string[] ignore)
{
int count = a.GetType().GetProperties().Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe();
bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb && ignore.Where(x => x == a.GetType().GetProperties()[i].Name).Count() == 0)
{
return false;
}
}
return true;
}
Verwendung:
if (MyFunction.Compare<ObjType>(a, b, "Id","AnotherProp"))
Das erste, was ich vorschlagen würde, wäre, den tatsächlichen Vergleich so aufzuteilen, dass er ein bisschen besser lesbar ist (ich habe auch den ToString () herausgenommen - wird das benötigt?):
else {
object originalProperty = sourceType.GetProperty(pi.Name).GetValue(this, null);
object comparisonProperty = destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null);
if (originalProperty != comparisonProperty)
return false;
Der nächste Vorschlag wäre, die Verwendung von Reflexion so weit wie möglich zu minimieren - es ist wirklich langsam. Ich meine, wirklich langsam. Wenn Sie dies tun, würde ich vorschlagen, die Eigenschaftsreferenzen zwischenzuspeichern. Ich bin mit der Reflection-API nicht sehr vertraut. Wenn dies nicht der Fall ist, passen Sie es einfach an, damit es kompiliert wird:
// elsewhere
Dictionary<object, Property[]> lookupDictionary = new Dictionary<object, Property[]>;
Property[] objectProperties = null;
if (lookupDictionary.ContainsKey(sourceType)) {
objectProperties = lookupProperties[sourceType];
} else {
// build array of Property references
PropertyInfo[] sourcePropertyInfos = sourceType.GetProperties();
Property[] sourceProperties = new Property[sourcePropertyInfos.length];
for (int i=0; i < sourcePropertyInfos.length; i++) {
sourceProperties[i] = sourceType.GetProperty(pi.Name);
}
// add to cache
objectProperties = sourceProperties;
lookupDictionary[object] = sourceProperties;
}
// loop through and compare against the instances
Allerdings muss ich sagen, dass ich mit den anderen Plakaten einverstanden bin. Dies riecht faul und ineffizient. Sie sollten stattdessen IComparable implementieren :-).
Sie können Ihren Code optimieren, indem Sie GetProperties nur einmal pro Typ aufrufen:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b, params string[] ignore)
{
var aProps = a.GetType().GetProperties();
var bProps = b.GetType().GetProperties();
int count = aProps.Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = aProps[i].GetValue(a, null).ToStringNullSafe();
bb = bProps[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb && ignore.Where(x => x == aProps[i].Name).Count() == 0)
{
return false;
}
}
return true;
}
hier wird eins überarbeitet, um null = null als gleich zu behandeln
private bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(ignore);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | 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 != null)
{
if (!selfValue.Equals(toValue))
return false;
}
else if (toValue != null)
return false;
}
}
return true;
}
return self == to;
}
Stellen Sie sicher, dass Objekte nicht null
sind.
Mit obj1
und obj2
:
if(obj1 == null )
{
return false;
}
return obj1.Equals( obj2 );
meine Lösung wurde von der obigen Antwort von Aras Alenin inspiriert, in der ich eine Ebene des Objektvergleichs und ein benutzerdefiniertes Objekt für Vergleichsergebnisse hinzugefügt habe. Ich bin auch daran interessiert, den Objektnamen mit dem Objektnamen zu erhalten:
public static IEnumerable<ObjectPropertyChanged> GetPublicSimplePropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored) where T : class
{
return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, true, null, null);
}
public static IReadOnlyList<ObjectPropertyChanged> GetPublicGenericPropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored) where T : class
{
return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, false, null, null);
}
/// <summary>
/// Gets the names of the public properties which values differs between first and second objects.
/// Considers 'simple' properties AND for complex properties without index, get the simple properties of the children objects.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="previous">The previous object.</param>
/// <param name="proposedChange">The second object which should be the new one.</param>
/// <param name="namesOfPropertiesToBeIgnored">The names of the properties to be ignored.</param>
/// <param name="simpleTypeOnly">if set to <c>true</c> consider simple types only.</param>
/// <param name="parentTypeString">The parent type string. Meant only for recursive call with simpleTypeOnly set to <c>true</c>.</param>
/// <param name="secondType">when calling recursively, the current type of T must be clearly defined here, as T will be more generic (using base class).</param>
/// <returns>
/// the names of the properties
/// </returns>
private static IReadOnlyList<ObjectPropertyChanged> GetPublicGenericPropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored, bool simpleTypeOnly, string parentTypeString, Type secondType) where T : class
{
List<ObjectPropertyChanged> propertiesChanged = new List<ObjectPropertyChanged>();
if (previous != null && proposedChange != null)
{
var type = secondType == null ? typeof(T) : secondType;
string typeStr = parentTypeString + type.Name + ".";
var ignoreList = namesOfPropertiesToBeIgnored.CreateList();
IEnumerable<IEnumerable<ObjectPropertyChanged>> genericPropertiesChanged =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name) && pi.GetIndexParameters().Length == 0
&& (!simpleTypeOnly || simpleTypeOnly && pi.PropertyType.IsSimpleType())
let firstValue = type.GetProperty(pi.Name).GetValue(previous, null)
let secondValue = type.GetProperty(pi.Name).GetValue(proposedChange, null)
where firstValue != secondValue && (firstValue == null || !firstValue.Equals(secondValue))
let subPropertiesChanged = simpleTypeOnly || pi.PropertyType.IsSimpleType()
? null
: GetPublicGenericPropertiesChanged(firstValue, secondValue, namesOfPropertiesToBeIgnored, true, typeStr, pi.PropertyType)
let objectPropertiesChanged = subPropertiesChanged != null && subPropertiesChanged.Count() > 0
? subPropertiesChanged
: (new ObjectPropertyChanged(proposedChange.ToString(), typeStr + pi.Name, firstValue.ToStringOrNull(), secondValue.ToStringOrNull())).CreateList()
select objectPropertiesChanged;
if (genericPropertiesChanged != null)
{ // get items from sub lists
genericPropertiesChanged.ForEach(a => propertiesChanged.AddRange(a));
}
}
return propertiesChanged;
}
Verwenden der folgenden Klasse zum Speichern von Vergleichsergebnissen
[System.Serializable]
public class ObjectPropertyChanged
{
public ObjectPropertyChanged(string objectId, string propertyName, string previousValue, string changedValue)
{
ObjectId = objectId;
PropertyName = propertyName;
PreviousValue = previousValue;
ProposedChangedValue = changedValue;
}
public string ObjectId { get; set; }
public string PropertyName { get; set; }
public string PreviousValue { get; set; }
public string ProposedChangedValue { get; set; }
}
Und ein Muster-Unit-Test:
[TestMethod()]
public void GetPublicGenericPropertiesChangedTest1()
{
// Define objects to test
Function func1 = new Function { Id = 1, Description = "func1" };
Function func2 = new Function { Id = 2, Description = "func2" };
FunctionAssignment funcAss1 = new FunctionAssignment
{
Function = func1,
Level = 1
};
FunctionAssignment funcAss2 = new FunctionAssignment
{
Function = func2,
Level = 2
};
// Main test: read properties changed
var propertiesChanged = Utils.GetPublicGenericPropertiesChanged(funcAss1, funcAss2, null);
Assert.IsNotNull(propertiesChanged);
Assert.IsTrue(propertiesChanged.Count == 3);
Assert.IsTrue(propertiesChanged[0].PropertyName == "FunctionAssignment.Function.Description");
Assert.IsTrue(propertiesChanged[1].PropertyName == "FunctionAssignment.Function.Id");
Assert.IsTrue(propertiesChanged[2].PropertyName == "FunctionAssignment.Level");
}
manchmal möchten Sie nicht alle öffentlichen Eigenschaften und nur eine Teilmenge davon vergleichen. In diesem Fall können Sie die Logik einfach verschieben, um die gewünschte Liste von Eigenschaften mit der abstrakten Klasse zu vergleichen
public abstract class ValueObject<T> where T : ValueObject<T>
{
protected abstract IEnumerable<object> GetAttributesToIncludeInEqualityCheck();
public override bool Equals(object other)
{
return Equals(other as T);
}
public bool Equals(T other)
{
if (other == null)
{
return false;
}
return GetAttributesToIncludeInEqualityCheck()
.SequenceEqual(other.GetAttributesToIncludeInEqualityCheck());
}
public static bool operator ==(ValueObject<T> left, ValueObject<T> right)
{
return Equals(left, right);
}
public static bool operator !=(ValueObject<T> left, ValueObject<T> right)
{
return !(left == right);
}
public override int GetHashCode()
{
int hash = 17;
foreach (var obj in this.GetAttributesToIncludeInEqualityCheck())
hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode());
return hash;
}
}
verwenden Sie diese abstrakte Klasse später, um die Objekte zu vergleichen
public class Meters : ValueObject<Meters>
{
...
protected decimal DistanceInMeters { get; private set; }
...
protected override IEnumerable<object> GetAttributesToIncludeInEqualityCheck()
{
return new List<Object> { DistanceInMeters };
}
}
Dies funktioniert auch dann, wenn die Objekte unterschiedlich sind. Sie können die Methoden in der Utilities-Klasse anpassen. Vielleicht möchten Sie auch private Eigenschaften vergleichen ...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
class ObjectA
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public DateTime PropertyD { get; set; }
public string FieldA;
public DateTime FieldB;
}
class ObjectB
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public DateTime PropertyD { get; set; }
public string FieldA;
public DateTime FieldB;
}
class Program
{
static void Main(string[] args)
{
// create two objects with same properties
ObjectA a = new ObjectA() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
ObjectB b = new ObjectB() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
// add fields to those objects
a.FieldA = "hello";
b.FieldA = "Something differnt";
if (a.ComparePropertiesTo(b))
{
Console.WriteLine("objects have the same properties");
}
else
{
Console.WriteLine("objects have diferent properties!");
}
if (a.CompareFieldsTo(b))
{
Console.WriteLine("objects have the same Fields");
}
else
{
Console.WriteLine("objects have diferent Fields!");
}
Console.Read();
}
}
public static class Utilities
{
public static bool ComparePropertiesTo(this Object a, Object b)
{
System.Reflection.PropertyInfo[] properties = a.GetType().GetProperties(); // get all the properties of object a
foreach (var property in properties)
{
var propertyName = property.Name;
var aValue = a.GetType().GetProperty(propertyName).GetValue(a, null);
object bValue;
try // try to get the same property from object b. maybe that property does
// not exist!
{
bValue = b.GetType().GetProperty(propertyName).GetValue(b, null);
}
catch
{
return false;
}
if (aValue == null && bValue == null)
continue;
if (aValue == null && bValue != null)
return false;
if (aValue != null && bValue == null)
return false;
// if properties do not match return false
if (aValue.GetHashCode() != bValue.GetHashCode())
{
return false;
}
}
return true;
}
public static bool CompareFieldsTo(this Object a, Object b)
{
System.Reflection.FieldInfo[] fields = a.GetType().GetFields(); // get all the properties of object a
foreach (var field in fields)
{
var fieldName = field.Name;
var aValue = a.GetType().GetField(fieldName).GetValue(a);
object bValue;
try // try to get the same property from object b. maybe that property does
// not exist!
{
bValue = b.GetType().GetField(fieldName).GetValue(b);
}
catch
{
return false;
}
if (aValue == null && bValue == null)
continue;
if (aValue == null && bValue != null)
return false;
if (aValue != null && bValue == null)
return false;
// if properties do not match return false
if (aValue.GetHashCode() != bValue.GetHashCode())
{
return false;
}
}
return true;
}
}
Um die Antwort von @nawfal zu erweitern, benutze ich diese Option, um Objekte verschiedener Typen in meinen Komponententests zu testen und gleiche Eigenschaftsnamen zu vergleichen. In meinem Fall Datenbankentität und DTO.
Wird in meinem Test so verwendet.
Assert.IsTrue(resultDto.PublicInstancePropertiesEqual(expectedEntity));
public static bool PublicInstancePropertiesEqual<T, Z>(this T self, Z to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = typeof(T);
var type2 = typeof(Z);
var ignoreList = new List<string>(ignore);
var unequalProperties =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name)
let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
let toValue = type2.GetProperty(pi.Name).GetValue(to, null)
where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
select selfValue;
return !unequalProperties.Any();
}
return self == null && to == null;
}
Update zu Livius Antwort oben - CompareObjects.DifferencesString ist veraltet.
Dies funktioniert gut in einem Unit-Test:
CompareLogic compareLogic = new CompareLogic();
ComparisonResult result = compareLogic.Compare(object1, object2);
Assert.IsTrue(result.AreEqual);
Diese Methode ruft properties
der Klasse ab und vergleicht die Werte für jedes property
. Wenn einer der Werte unterschiedlich ist, wird er return false
, Andernfalls wird er return true
.
public static bool Compare<T>(T Object1, T object2)
{
//Get the type of the object
Type type = typeof(T);
//return false if any of the object is false
if (Object1 == null || object2 == null)
return false;
//Loop through each properties inside class and get values for the property from both the objects and compare
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
if (property.Name != "ExtensionData")
{
string Object1Value = string.Empty;
string Object2Value = string.Empty;
if (type.GetProperty(property.Name).GetValue(Object1, null) != null)
Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString();
if (type.GetProperty(property.Name).GetValue(object2, null) != null)
Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString();
if (Object1Value.Trim() != Object2Value.Trim())
{
return false;
}
}
}
return true;
}
Verwendung:
bool isEqual = Compare<Employee>(Object1, Object2)