जवाबों:
लागू करें IEquatable<T>(आमतौर पर अपने सभी कस्टम प्रकारों पर विरासत में मिली Object.Equalsऔर Object.GetHashCodeतरीकों को पछाड़कर )। मिश्रित प्रकारों के मामले में, समाहित प्रकारों के Equalsभीतर निहित प्रकारों की विधि लागू करें । निहित संग्रहों के लिए, SequenceEqualविस्तार विधि का उपयोग करें , जो आंतरिक रूप से IEquatable<T>.Equalsया Object.Equalsप्रत्येक तत्व पर कॉल करता है। इस दृष्टिकोण को स्पष्ट रूप से आपको अपने प्रकार की परिभाषाओं का विस्तार करने की आवश्यकता होगी, लेकिन इसके परिणाम क्रमिकता वाले किसी भी सामान्य समाधान की तुलना में तेज़ हैं।
संपादित करें : यहां नेस्टिंग के तीन स्तरों के साथ एक आकस्मिक उदाहरण है।
मान प्रकारों के लिए, आप आमतौर पर केवल उनके Equalsतरीके को कॉल कर सकते हैं । यहां तक कि अगर फ़ील्ड या गुण कभी भी स्पष्ट रूप से असाइन नहीं किए गए थे, तब भी उनके पास एक डिफ़ॉल्ट मान होगा।
संदर्भ प्रकारों के लिए, आपको पहले कॉल करना चाहिए ReferenceEquals, जो संदर्भ समानता के लिए जांच करता है - यह एक दक्षता को बढ़ावा देने के रूप में काम करेगा जब आप उसी वस्तु को संदर्भित करते हैं। यह उन मामलों को भी हैंडल करेगा जहां दोनों संदर्भ शून्य हैं। यदि वह चेक विफल हो जाता है, तो पुष्टि करें कि आपके इंस्टेंस का क्षेत्र या संपत्ति शून्य नहीं है (बचने के लिए NullReferenceException) और इसकी Equalsविधि को कॉल करें । चूंकि हमारे सदस्य ठीक से टाइप किए गए हैं, इसलिए IEquatable<T>.Equalsओवरराइड Object.Equalsविधि (जिसका निष्पादन टाइप कास्ट के कारण मामूली धीमा होगा) को दरकिनार करते हुए, विधि को सीधे बुलाया जाता है ।
जब आप ओवरराइड करते हैं Object.Equals, तो आपको ओवरराइड करने की भी उम्मीद होती है Object.GetHashCode; मैं संक्षिप्तता की खातिर ऐसा नीचे नहीं करता था।
public class Person : IEquatable<Person>
{
public int Age { get; set; }
public string FirstName { get; set; }
public Address Address { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public bool Equals(Person other)
{
if (other == null)
return false;
return this.Age.Equals(other.Age) &&
(
object.ReferenceEquals(this.FirstName, other.FirstName) ||
this.FirstName != null &&
this.FirstName.Equals(other.FirstName)
) &&
(
object.ReferenceEquals(this.Address, other.Address) ||
this.Address != null &&
this.Address.Equals(other.Address)
);
}
}
public class Address : IEquatable<Address>
{
public int HouseNo { get; set; }
public string Street { get; set; }
public City City { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Address);
}
public bool Equals(Address other)
{
if (other == null)
return false;
return this.HouseNo.Equals(other.HouseNo) &&
(
object.ReferenceEquals(this.Street, other.Street) ||
this.Street != null &&
this.Street.Equals(other.Street)
) &&
(
object.ReferenceEquals(this.City, other.City) ||
this.City != null &&
this.City.Equals(other.City)
);
}
}
public class City : IEquatable<City>
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as City);
}
public bool Equals(City other)
{
if (other == null)
return false;
return
object.ReferenceEquals(this.Name, other.Name) ||
this.Name != null &&
this.Name.Equals(other.Name);
}
}
अद्यतन : यह उत्तर कई साल पहले लिखा गया था। तब से, मैंने IEquality<T>ऐसे परिदृश्यों के लिए परिवर्तनशील प्रकारों को लागू करने से दूर रहना शुरू कर दिया है। : वहाँ समानता के दो विचार कर रहे हैं पहचान और तुल्यता । एक मेमोरी प्रतिनिधित्व स्तर पर, ये "संदर्भ समानता" और "मूल्य समानता" ( समानता तुलना देखें ) के रूप में लोकप्रिय रूप से प्रतिष्ठित हैं । हालाँकि, समान अंतर एक डोमेन स्तर पर भी लागू हो सकता है। मान लीजिए कि आपकी Personकक्षा में एक PersonIdसंपत्ति है, जो विशिष्ट वास्तविक दुनिया के प्रति अद्वितीय है। क्या एक ही PersonIdलेकिन भिन्न Ageमान वाली दो वस्तुओं को समान या अलग माना जाना चाहिए? उपरोक्त उत्तर मानता है कि एक तुल्यता के बाद है। हालाँकि, के कई usages हैंIEquality<T>इंटरफ़ेस, जैसे संग्रह, कि मान लेते हैं कि इस तरह के कार्यान्वयन पहचान के लिए प्रदान करते हैं । उदाहरण के लिए, यदि आप आबादी कर रहे हैं, तो आप HashSet<T>आमतौर TryGetValue(T,T)पर मौजूदा तत्वों को वापस करने के लिए एक कॉल की उम्मीद करेंगे जो आपके तर्क की पहचान को साझा करते हैं, जरूरी नहीं कि समान तत्व जिनकी सामग्री पूरी तरह से समान हो। इस धारणा को नोटों द्वारा लागू किया गया है GetHashCode:
सामान्य तौर पर, परिवर्तनशील संदर्भ प्रकारों के लिए, आपको
GetHashCode()केवल तभी ओवरराइड करना चाहिए :
- आप उन फ़ील्ड से हैश कोड की गणना कर सकते हैं जो परस्पर नहीं हैं; या
- आप यह सुनिश्चित कर सकते हैं कि उत्परिवर्तित वस्तु का हैश कोड परिवर्तित नहीं होता है, जबकि ऑब्जेक्ट एक संग्रह में निहित होता है जो उसके हैश कोड पर निर्भर करता है।
partial- जिस स्थिति में, हां, आप Equalsमैन्युअल रूप से जोड़े गए आंशिक वर्ग घोषणा के माध्यम से उनकी विधि को लागू कर सकते हैं, जो स्वतः-जनरेट किए गए फ़ील्ड / गुणों को संदर्भित करता है। एक।
Enumerable.SequenceEqualसरणियों पर LINQ विधि कह सकते हैं this.Addresses.SequenceEqual(other.Addresses):। यह आंतरिक रूप Address.Equalsसे संबंधित पतों के प्रत्येक जोड़े के लिए आपके तरीके को कॉल करेगा , यह देखते हुए कि Addressक्लास IEquatable<Address>इंटरफ़ेस को लागू करता है।
दोनों वस्तुओं को क्रमबद्ध करें और परिणामी तारों की तुलना करें
+1यह सिर्फ इसलिए कर रहा हूं क्योंकि मैंने इस तरह मूल्यों-आधारित समानता की तुलना करने के बारे में कभी नहीं सोचा है। यह अच्छा और सरल है। इस कोड के साथ कुछ बेंचमार्क देखना साफ-सुथरा होगा।
आप इस समस्या को हल करने के लिए विस्तार विधि, पुनरावृत्ति का उपयोग कर सकते हैं:
public static bool DeepCompare(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
//Compare two object's class, return false if they are difference
if (obj.GetType() != another.GetType()) return false;
var result = true;
//Get all properties of obj
//And compare each other
foreach (var property in obj.GetType().GetProperties())
{
var objValue = property.GetValue(obj);
var anotherValue = property.GetValue(another);
if (!objValue.Equals(anotherValue)) result = false;
}
return result;
}
public static bool CompareEx(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
//properties: int, double, DateTime, etc, not class
if (!obj.GetType().IsClass) return obj.Equals(another);
var result = true;
foreach (var property in obj.GetType().GetProperties())
{
var objValue = property.GetValue(obj);
var anotherValue = property.GetValue(another);
//Recursion
if (!objValue.DeepCompare(anotherValue)) result = false;
}
return result;
}
या Json का उपयोग करके तुलना करें (यदि ऑब्जेक्ट बहुत जटिल है) तो आप Newtonsoft.Json का उपयोग कर सकते हैं:
public static bool JsonCompare(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
var objJson = JsonConvert.SerializeObject(obj);
var anotherJson = JsonConvert.SerializeObject(another);
return objJson == anotherJson;
}
DeepCompareकेवल CompareExपुनरावर्ती कॉल करने के बजाय उपयोग करने का कोई कारण है ?
resultसाथ प्रतिस्थापित return falseकरने से यह अधिक कुशल हो जाएगा।
यदि आप IEquatable को लागू नहीं करना चाहते हैं, तो आप हमेशा सभी गुणों की तुलना करने के लिए परावर्तन का उपयोग कर सकते हैं: - यदि वे मूल्य प्रकार हैं, तो बस उनकी तुलना करें-यदि वे संदर्भ प्रकार हैं, तो इसके "आंतरिक" गुणों की तुलना करने के लिए फ़ंक्शन को पुन: कॉल करें। ।
मैं प्रदर्शन के बारे में नहीं सोच रहा हूं, बल्कि सादगी के बारे में सोच रहा हूं। यह निर्भर करता है, हालांकि आपकी वस्तुओं के सटीक डिजाइन पर। यह आपकी वस्तुओं के आकार के आधार पर जटिल हो सकता है (उदाहरण के लिए यदि गुणों के बीच चक्रीय निर्भरताएं हैं)। हालाँकि, वहाँ कई समाधान हैं जो आप उपयोग कर सकते हैं, जैसे कि यह एक:
एक अन्य विकल्प ऑब्जेक्ट को टेक्स्ट के रूप में क्रमबद्ध करना है, उदाहरण के लिए JSON.NET का उपयोग करना, और क्रमांकन परिणाम की तुलना करना। (JSON.NET संपत्तियों के बीच चक्रीय निर्भरता को संभाल सकता है)।
मुझे नहीं पता कि अगर आप सबसे तेज़ हैं तो इसका मतलब है कि इसे लागू करने का सबसे तेज़ तरीका या तेज़ चलने वाला कोड। जरूरत पड़ने पर आपको जानने से पहले अनुकूलन नहीं करना चाहिए। सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है
IEquatable<T>कार्यान्वयन समयपूर्व अनुकूलन के मामले के रूप में योग्य है। परावर्तन काफी धीमा होगा। Equalsकस्टम मूल्य प्रकारों के लिए डिफ़ॉल्ट कार्यान्वयन प्रतिबिंब का उपयोग करता है; Microsoft खुद को प्रदर्शन के लिए इसे ओवरराइड करने की सलाह देता है: " Equalsविधि के प्रदर्शन को बेहतर बनाने के लिए एक विशेष प्रकार के लिए विधि को ओवरराइड करें और अधिक बारीकी से प्रकार के लिए समानता की अवधारणा का प्रतिनिधित्व करें।"
दोनों वस्तुओं को क्रमबद्ध करें और @JoelFan द्वारा परिणामी तारों की तुलना करें
तो ऐसा करने के लिए, एक स्टैटिक क्लास बनाएं जैसे कि और सभी वस्तुओं का विस्तार करने के लिए एक्सटेंशन्स का उपयोग करें (ताकि आप विधि, संग्रह, आदि के किसी भी तरीके को पास कर सकें)
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
public static class MySerializer
{
public static string Serialize(this object obj)
{
var serializer = new DataContractJsonSerializer(obj.GetType());
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.Default.GetString(ms.ToArray());
}
}
}
एक बार जब आप किसी अन्य फ़ाइल में इस स्थिर वर्ग का संदर्भ देते हैं, तो आप यह कर सकते हैं:
Person p = new Person { Firstname = "Jason", LastName = "Argonauts" };
Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" };
//assuming you have already created a class person!
string personString = p.Serialize();
string person2String = p2.Serialize();
अब आप बस इनकी तुलना करने के लिए उपयोग कर सकते हैं। मैं यह जाँचने के लिए उपयोग करता हूँ कि क्या वस्तुएँ संग्रह में भी हैं। यह वास्तव में अच्छी तरह से काम करता है।
CultrureInfo। यह केवल तभी काम करेगा जब आंतरिक डेटा ज्यादातर तार और पूर्णांक है। नहीं तो अनर्थ हो जाता।
मुझे लगता है आप शाब्दिक रूप से एक ही वस्तुओं का जिक्र नहीं कर रहे हैं
Object1 == Object2
आप दोनों के बीच मेमोरी तुलना करने के बारे में सोच रहे होंगे
memcmp(Object1, Object2, sizeof(Object.GetType())
लेकिन यह सी # :) में भी वास्तविक कोड नहीं है। क्योंकि आपका सारा डेटा संभवतः ढेर पर बनाया गया है, स्मृति सन्निहित नहीं है और आप केवल अज्ञेय तरीके से दो वस्तुओं की समानता की तुलना नहीं कर सकते हैं। आप प्रत्येक मान की, एक समय में, एक कस्टम तरीके से तुलना करने जा रहे हैं।
IEquatable<T>अपनी कक्षा में इंटरफ़ेस जोड़ने पर विचार करें , और Equalsअपने प्रकार के लिए एक कस्टम विधि निर्धारित करें। फिर, उस विधि में, मैन्युअल प्रत्येक मान का परीक्षण करें। IEquatable<T>संलग्न प्रकारों पर फिर से जोड़ें यदि आप प्रक्रिया को दोहरा सकते हैं।
class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
/* check all the values */
return false;
}
}
दोनों वस्तुओं को क्रमबद्ध करें, फिर हैश कोड की गणना करें, फिर तुलना करें।
मुझे वस्तुओं की तुलना करने के लिए यह नीचे फ़ंक्शन मिला।
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 (object.Equals(Object1, default(T)) || object.Equals(object2, default(T)))
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;
}
मैं इसका उपयोग कर रहा हूं और यह मेरे लिए ठीक काम कर रहा है।
ifमतलब है कि Compare(null, null) == falseमैं क्या उम्मीद नहीं है।
पहले से ही यहां दिए गए कुछ उत्तरों के आधार पर मैंने ज्यादातर जोएलफैन के जवाब को वापस लेने का फैसला किया । मुझे विस्तार के तरीकों से प्यार है और ये मेरे लिए बहुत अच्छा काम कर रहे हैं जब कोई भी अन्य समाधान मेरी जटिल कक्षाओं की तुलना करने के लिए उनका उपयोग नहीं करेगा।
using System.IO;
using System.Xml.Serialization;
static class ObjectHelpers
{
public static string SerializeObject<T>(this T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
public static bool EqualTo(this object obj, object toCompare)
{
if (obj.SerializeObject() == toCompare.SerializeObject())
return true;
else
return false;
}
public static bool IsBlank<T>(this T obj) where T: new()
{
T blank = new T();
T newObj = ((T)obj);
if (newObj.SerializeObject() == blank.SerializeObject())
return true;
else
return false;
}
}
if (record.IsBlank())
throw new Exception("Record found is blank.");
if (record.EqualTo(new record()))
throw new Exception("Record found is blank.");
मैं कहूँगा कि:
Object1.Equals(Object2)
वह होगा जो आप खोज रहे हैं। यदि आप देख रहे हैं कि क्या वस्तुएं समान हैं, तो वही है जो आप पूछ रहे हैं।
यदि आप यह देखना चाहते हैं कि क्या सभी चाइल्ड ऑब्जेक्ट समान हैं, तो उन्हें Equals()विधि के साथ लूप के माध्यम से चलाएं ।
public class GetObjectsComparison
{
public object FirstObject, SecondObject;
public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
}
public struct SetObjectsComparison
{
public FieldInfo SecondObjectFieldInfo;
public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue;
public bool ErrorFound;
public GetObjectsComparison GetObjectsComparison;
}
private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison)
{
GetObjectsComparison FunctionGet = GetObjectsComparison;
SetObjectsComparison FunctionSet = new SetObjectsComparison();
if (FunctionSet.ErrorFound==false)
foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions))
{
FunctionSet.SecondObjectFieldInfo =
FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions);
FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject);
FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject);
if (FirstObjectFieldInfo.FieldType.IsNested)
{
FunctionSet.GetObjectsComparison =
new GetObjectsComparison()
{
FirstObject = FunctionSet.FirstObjectFieldInfoValue
,
SecondObject = FunctionSet.SecondObjectFieldInfoValue
};
if (!ObjectsComparison(FunctionSet.GetObjectsComparison))
{
FunctionSet.ErrorFound = true;
break;
}
}
else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue)
{
FunctionSet.ErrorFound = true;
break;
}
}
return !FunctionSet.ErrorFound;
}
ऐसा करने का एक तरीका यह होगा कि Equals()इसमें शामिल प्रत्येक प्रकार पर ओवरराइड किया जाए। उदाहरण के लिए, आपकी शीर्ष स्तर की वस्तु सभी 5 चाइल्ड ऑब्जेक्ट Equals()की Equals()विधि को कॉल करने के लिए ओवरराइड होगी । उन वस्तुओं को पूरी Equals()तरह से ओवरराइड करना चाहिए , यह मानते हुए कि वे कस्टम ऑब्जेक्ट हैं, और इसी तरह जब तक पूरे पदानुक्रम की तुलना सिर्फ शीर्ष स्तर की वस्तुओं पर समानता की जांच करने से नहीं की जा सकती।
IEquatable<T>इंटरफ़ेस का उपयोग करें जिसमें एक विधि है Equals।
जोनाथन के उदाहरण के लिए धन्यवाद। मैंने इसे सभी मामलों (सरणियों, सूचियों, शब्दकोशों, आदिम प्रकारों) के लिए विस्तारित किया।
यह क्रमबद्धता के बिना एक तुलना है और तुलनात्मक वस्तुओं के लिए किसी भी इंटरफेस के कार्यान्वयन की आवश्यकता नहीं है।
/// <summary>Returns description of difference or empty value if equal</summary>
public static string Compare(object obj1, object obj2, string path = "")
{
string path1 = string.IsNullOrEmpty(path) ? "" : path + ": ";
if (obj1 == null && obj2 != null)
return path1 + "null != not null";
else if (obj2 == null && obj1 != null)
return path1 + "not null != null";
else if (obj1 == null && obj2 == null)
return null;
if (!obj1.GetType().Equals(obj2.GetType()))
return "different types: " + obj1.GetType() + " and " + obj2.GetType();
Type type = obj1.GetType();
if (path == "")
path = type.Name;
if (type.IsPrimitive || typeof(string).Equals(type))
{
if (!obj1.Equals(obj2))
return path1 + "'" + obj1 + "' != '" + obj2 + "'";
return null;
}
if (type.IsArray)
{
Array first = obj1 as Array;
Array second = obj2 as Array;
if (first.Length != second.Length)
return path1 + "array size differs (" + first.Length + " vs " + second.Length + ")";
var en = first.GetEnumerator();
int i = 0;
while (en.MoveNext())
{
string res = Compare(en.Current, second.GetValue(i), path);
if (res != null)
return res + " (Index " + i + ")";
i++;
}
}
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type))
{
System.Collections.IEnumerable first = obj1 as System.Collections.IEnumerable;
System.Collections.IEnumerable second = obj2 as System.Collections.IEnumerable;
var en = first.GetEnumerator();
var en2 = second.GetEnumerator();
int i = 0;
while (en.MoveNext())
{
if (!en2.MoveNext())
return path + ": enumerable size differs";
string res = Compare(en.Current, en2.Current, path);
if (res != null)
return res + " (Index " + i + ")";
i++;
}
}
else
{
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
{
try
{
var val = pi.GetValue(obj1);
var tval = pi.GetValue(obj2);
if (path.EndsWith("." + pi.Name))
return null;
var pathNew = (path.Length == 0 ? "" : path + ".") + pi.Name;
string res = Compare(val, tval, pathNew);
if (res != null)
return res;
}
catch (TargetParameterCountException)
{
//index property
}
}
foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
{
var val = fi.GetValue(obj1);
var tval = fi.GetValue(obj2);
if (path.EndsWith("." + fi.Name))
return null;
var pathNew = (path.Length == 0 ? "" : path + ".") + fi.Name;
string res = Compare(val, tval, pathNew);
if (res != null)
return res;
}
}
return null;
}
कोड की आसान नकल के लिए रिपोजिटरी बनाया
अब आप json.net का उपयोग कर सकते हैं। बस Nuget पर जाएं और इसे इंस्टॉल करें।
और आप ऐसा कुछ कर सकते हैं:
public bool Equals(SamplesItem sampleToCompare)
{
string myself = JsonConvert.SerializeObject(this);
string other = JsonConvert.SerializeObject(sampleToCompare);
return myself == other;
}
यदि आप कट्टरता प्राप्त करना चाहते हैं तो आप शायद ऑब्जेक्ट के लिए एक विस्तार विधि बना सकते हैं। कृपया ध्यान दें कि यह केवल सार्वजनिक संपत्तियों की तुलना करता है। और यदि आप एक सार्वजनिक संपत्ति को अनदेखा करना चाहते थे जब आप तुलना करते हैं तो आप [JsonIgnore] विशेषता का उपयोग कर सकते हैं।