EDIT:
What I'm trying to do is to find if db.Id is equal to xml.Id and db.SubTitle is equal to xml.SubTitle ....etc.....all my prop
also I did tried
bool result = db.SequenceEqual(xml) it returns false all the time.
ENd EDIT
I did search before I end-up asking for help and I'm not sure what is the best way to approach to my problem.
I have two IList objects and both have exact same property but the data might be different.
one object is populating from db and other is reading from xml to compare both source is in sync.
here is my object looks like:
public class EmployeeObject
{
public Int32 Id { get; set; }
public string SubTitle { get; set; }
public string Desc { get; set; }
public bool Active { get; set; }
public string ActiveDateTime { get; set; }
}
here is what I have tried:
IList<EmployeeObject> db = Db.EmployeeRepository.PopulateFromDb();
IList<EmployeeObject> xml = Xml.EmployeeRepository.PopulateFromXml();
//both object populated with data so far so good....
Time to compare now:
I have tried some thing like this:
if ((object)xml == null || ((object)db) == null)
return Object.Equals(xml, db);
return xml.Equals(db); // returning false all the time
i have checked both object has the exact same data and but still returning false
The Equals method that you are using is going to determine if the two references refer to the same list, not if the contents are the same. You can use SequenceEqual to actually verify that two sequences have the same items in the same order.
Next you'll run into the issue that each item in the list will be compared to see if they refer to the same object, rather than containing the same field values, or the same ID values, as seems to be the what you want here. One option is a custom comparer, but another is to pull out the "identity" object in question:
bool areEqual = db.Select(item => item.id)
.SequenceEqual(xml.Select(item => item.id));
You should override Equals and GetHashCode in your class like this:
public class EmployeeObject {
public Int32 Id { get; set; }
public string SubTitle { get; set; }
public string Desc { get; set; }
public bool Active { get; set; }
public string ActiveDateTime { get; set; }
public override bool Equals(object o){
EmployeeObject e = o as EmployeeObject;
if(e == null) return false;
return Id == e.Id && SubTitle == e.SubTitle && Desc == e.Desc
&& Active == e.Active && ActiveDateTime == e.ActiveDateTime;
}
public override int GetHashCode(){
return Id.GetHashCode() ^ SubTitle.GetHashCode() ^ Desc.GetHashCode()
^ Active.GetHashCode() ^ ActiveDateTime.GetHashCode();
}
}
Then use the SequenceEqual method:
return db.OrderBy(e=>e.Id).SequenceEqual(xml.OrderBy(e=>e.Id));
IList does not have an Equals method. What you're calling is the standard Object equals which checks whether two variables point to the same object or not.
If you want to check that the lists are semantically equivalent, you will need to check that each object in the list is equivalent. If the EmployeeObject class has an appropriate Equals method, then you can use SequenceEquals to compare the lists.
You can implement an IEqualityComparer and use the overload of SequenceEquals that takes an IEqualityComparer. Here is sample code for an IEqualityComparer from msdn:
class BoxEqualityComparer : IEqualityComparer<Box>
{
public bool Equals(Box b1, Box b2)
{
if (b1.Height == b2.Height && b1.Length == b2.Length && b1.Width == b2.Width)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Box bx)
{
int hCode = bx.Height ^ bx.Length ^ bx.Width;
return hCode.GetHashCode();
}
}
You can then use SequenceEquals like this:
if (db.SequnceEquals(xml), new MyEqualityComparer())
{ /* Logic here */ }
Note that this will only return true if the items also are ordered in the same order in the lists. If appropriate, you can pre-order the items like this:
if (db.OrderBy(item => item.id).SequnceEquals(xml.OrderBy(item => item.id)), new MyEqualityComparer())
{ /* Logic here */ }
Obviously, the return of return xml.Equals(db); will always be false if you are comparing two different lists.
The only way for this to make sense is for you to actually be more specific about what it means for those two lists to be equal. That is you need to go through the elements in the two lists and ensure that the lists both contain the same items. Even that is ambiguous but assuming that the elements in your provide a proper override for Equals() and GetHashCode() then you can proceed to implement that actual list comparison.
Generally, the most efficient method to compare two lists that don't contain duplicates will be to use a hash set constructed from elements of one of the lists and then iterate through the elements of the second, testing whether each element is found in the hash set.
If the lists contain duplicates your best bet is going to be to sort them both and then walk the lists in tandem ensuring that the elements at each point match up.
You can use SequenceEqual provided you can actually compare instances of EmployeeObject. You probably have to Equals on EmployeeObject:
public override bool Equals(object o)
{
EmployeeObject obj = o as EmployeeObject;
if(obj == null) return false;
// Return true if all the properties match
return (Id == obj.Id &&
SubTitle == obj.SubTitle &&
Desc == obj.Desc &&
Active == obj.Active &&
ActiveDateTime == obj.ActiveDateTime);
}
Then you can do:
var same = db.SequenceEqual(xml);
You can also pass in a class that implements IEqualityComparer which instructs SequenceEqual how to compare each instance:
var same = db.SequenceEqual(xml, someComparer);
Another quick way, though not as fast, would be to build two enumerations of the value you want to compare, probably the id property in your case:
var ids1 = db.Select(i => i.Id); // List of all Ids in db
var ids2 = xml.Select(i => i.Id); // List of all Ids in xml
var same = ids1.SequenceEqual(ids2); // Both lists are the same
Related
I have two list
class obj1
{
public string country{ get; set; }
public string region{ get; set; }
}
class obj2
{
public string country{ get; set; }
public string region { get; set; }
public string XYZ { get; set; }
public bool ToBeChanged{ get; set; }
}
first list looks like:
List<obj1> alist = new List<obj1>();
alist.Add("US", "NC");
alist.Add("US", "SC");
alist.Add("US", "NY");
second list (List<obj2> alist2) may make 1000 of entries with many combination of country and region.
I need to update the property "ToBeChanged" to "True" if second (alist2) list properties (country and region) matches to first(alist1) and false in otherwise.
Please help.
Thanks,
Vaibhav
Two points from the comments, and my thoughts:
Some aren't sure exactly what your matching criteria is. But to me it seems fairly clear that you're matching on 'country' and 'region'. Nevertheless, in the future, state this explicitly.
You got one comment criticizing your choice of variable names. That criticism is fully justified. Code is far easier to maintain when you have little hints as to what it's doing, and variable names are crucial for that.
Now, regarding my particular solution:
In the code below, I've renamed some of your objects to make them clear in their purpose. I'd like to rename 'obj2', but I'll leave that to you because I'm not exactly sure what you're intending to do with it, and I definitely don't know what 'XYZ' is for. Here are the renamed classes, with some added constructors to aid in list construction.
class RegionInfo {
public RegionInfo(string country, string region) {
this.country = country;
this.region = region;
}
public string country{ get; set; }
public string region{ get; set; }
}
class obj2 {
public obj2 (string country, string region, string XYZ) {
this.country = country;
this.region = region;
this.XYZ = XYZ;
}
public string country{ get; set; }
public string region { get; set; }
public string XYZ { get; set; }
public bool ToBeChanged{ get; set; }
}
I'm using a LINQ Join to match the two lists, outputting only the 'obj2' side of the join, and then looping the result to toggle the 'ToBeChanged' value.
var regionInfos = new List<RegionInfo>() {
new RegionInfo("US", "NC"),
new RegionInfo("US", "SC"),
new RegionInfo("US", "NY")
};
var obj2s = new List<obj2> {
new obj2("US", "NC", "What am I for?"),
new obj2("US", "SC", "Like, am I supposed to be the new value?"),
new obj2("CA", "OT", "XYZ doesn't have a stated purpose")
};
var obj2sToChange = obj2s
.Join(
regionInfos,
o2 => new { o2.country, o2.region },
reg => new { reg.country, reg.region },
(o2,reg) => o2
);
foreach (var obj2 in obj2sToChange)
obj2.ToBeChanged = true;
obj2s.Dump(); // using Linqpad, but you do what works to display
This results in:
country
region
XYZ
ToBeChanged
US
NC
What am I for?
True
US
SC
Like, am I supposed to be the new value?
True
CA
OT
XYZ doesn't have a stated purpose
False
First of all, with LINQ you can never change the source. You can only extract data from the source. After that you can use the extracted data to update the source.
I need to update the property "ToBeChanged" to "True" if second (alist2) list properties (country and region) matches to first(alist1) and false in otherwise.
This is not a proper requirement. alist1 is a sequence of obj1 objects. I think, that you want the property ToBeChanged of a certain obj2 to be true if any of the obj1 items in alist1 has a [country, region] combination that matches the [country, region] combination of the obj2 concerned.
requirement Get all obj2 in alist2, that have a [country, region] combination that matches any of the [country, region] combinations of the obj1 objects in alist1.
You probably thought about using Where for this. Something like "Where [country, region] combination in the other list". Whenever you need to find out if an item is in another list, consider to use one of the overloads of Enumerable.Contains
The problem is, that the [Country, Region] combination in every obj2 can be converted to an object of class obj1, but if you want to check if they are equal, you will have a compare by reference, while you want a compare by value.
There are two solutions for this:
create an EqualityComparer that compares obj1 by Value
create [Country, Region] as anonymous type. Anonymous types always compare by value.
The latter is the most easy, so we'll do that one first.
Use anonymous types for comparison
First convert alist into anonymous type containing [Country, Region] combinations:
var eligibleCountryRegionCombinations = alist.Select(obj1 => new
{
Country = obj1.Country,
Region = obj1.Region,
});
Note that I don't use ToList at the end: the enumerable is created, but the sequence has not been enumerated yet. In LINQ terms this is called lazy or deferred execution.
IEnumerable<obj2> obj2sThatNeedToBeChanged = alist2.Select(obj2 => new
{
CountryRegionCombination = new
{
Country = obj2.Country,
Region = obj2.Region,
},
Original = obj2,
})
.Where(item => eligibleCountryRegionCombinations.Contains(
item.CountryRegionCombination))
.Select(item => item.Original);
CountryRegionCombination is an anonymous type of the same type as the anonymous items in eligibleCountryRegionCombinations. Therefore you can use Contains. Because the items are anonymous type, the equality comparison is comparison by value.
The final select will remove the anonymous type, and keep only the Original.
Note that the query is still not enumerated.
foreach (var obj2 in obj2sThatNeedToBeChanged.ToList())
{
obj2.ToBeChanged = true;
}
It can be dangerous to change the source that you are enumerating. In this case it is not a problem, because the field that you change is not used to create the enumeration. Still I think it is safer, because of possible future changes, to do a ToList before you start changing the source.
Create an equality comparer
One of the overload of Enumerable.Contains has a parameter comparer. This expects an IEqualityComparer<obj1>
class Obj1Comparer : EqualityComparer<obj1>
{
public static IEqualityComparer<obj1> ByValue {get;} = new Obj1Comparer();
private static IEqualityComparer<string> CountryComparer => StringComparer.OrdinalIgnoreCase;
private static IEqualityComparer<string> RegionComparer => StringComparer.OrdinalIgnoreCase;
public override bool Equals (obj1 x, obj1 y)
{
if (x == null) return y == null; // true if both null, false if x null, but y not null
if (y == null) return false; // because x not null
// optimization:
if (Object.ReferenceEquals(x, y)) return true;
if (x.GetType() != y.GetType()) return false;
return CountryComparer.Equals(x.Country, y.Country)
&& RegionComparer.Equals(x.Region, y.Region);
}
To make it easy to change equality of countries, I created a separate comparer for countries and for regions. So if later you want to compare case sensitive, or if you change Country from string to a foreign key to a table of countries, then changes will be minimal.
You also need to override GetHashCode. If x equals y, then GetHashCode should rerturn the same value. Not the other way round: if x and y different they may return the same hash code. However, code will be more efficient if you have more different Hash codes.
public override int GetHashCode (obj1 x)
{
if (x == null) return 87966354; // just a number
return CountryComparer.GetHashCode(x.Country)
^ RegionComparer.GetHashCode(x.Region);
}
Which HashCode you return depends on how often this will be called, for instance in dictionaries, comparers like Contains, etc.
How "different" are the Countries and Regions? A different Country will probably also mean a different region. So maybe it is efficient enough if you only calculate the Hash code for the Country. If a Country has many, many regions, then it will probably be better to calculate the hash code for regions as well If a Region is only in one Country (OberAmmerGau is probably only in Germany), or in only a few Regions (how many regions "New Amsterdam" will there be?), then you won't have to check the Country at all.
Because we have an equality comparer, we don't need to convert alist to an anonymous type, we can specify that Contains should compare by value.
IEqualityComparer<obj1> comparer = Obj1Comparer.ByValue;
IEnumerable<obj2> obj2sThatNeedToBeChanged = alist2.Select(obj2 => new
{
Obj1 = new Obj1
{
Country = obj2.Country,
Region = obj2.Region,
},
Original = obj2,
})
.Where(item => alist.Contains(item.CountryRegionCombination, comparer))
.Select(item => item.Original);
Fast method: Extension method
The fastest method, and maybe also the most simple one, is to create an extension method.
private static IEqualityComparer<string> CountryComparer => StringComparer.OrdinalIgnoreCase;
private static IEqualityComparer<string> RegionComparer => StringComparer.OrdinalIgnoreCase;
public static IEnumerable<Obj2> WhereSameLocation(
this IEnumerable<Obj2> source,
IEnumerable<Obj1> obj1Items)
{
// TODO: what to do if source == null?
foreach (Obj2 obj2 in source)
{
// check if there is any obj1 with same [Country, Region]
if (obj1Items
.Where(obj1 => CountryComparer.Equals(obj2.Country, obj1.Country)
&& RegionComparer.Equals(obj2.Region, obj1.Region))
.Any())
{
yield return obj2;
}
}
}
Usage:
IEnumerable<Obj1> alist = ...
IEnumerable<Obj2> alist2 = ...
IEnumerable<obj2> obj2sThatNeedToBeChanged = alist2.WhereSameLocation(alist);
I need to compare two List<object> but during comparison for properties having "string" value I don't want case sensitive comparison.
I have a class:
class User
{
public int Id { get;set; }
public string name { get;set; }
}
I have 2 lists List<User> olduser and List<User> newuser. I need to compare both lists but while comparing I should ignore case sensitivity of "name" field and get values in olduser not part of values in newuser.
List<User> obsoleteUsers = olduser.Except(newuser).ToList();
I need to add a condition that while comparing two lists, please ignore the case for "name" field.
You can use a custom IEqualityComparer<T>:
class UserNameComparer : IEqualityComparer<User>
{
public UserNameComparer(StringComparer comparer)
{
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
this.Comparer = comparer;
}
public StringComparer Comparer { get; }
public bool Equals(User x, User y)
{
if (x == null || y == null) return true;
return Comparer.Equals(x.name, y.name);
}
public int GetHashCode(User obj)
{
return Comparer.GetHashCode(obj?.name);
}
}
You use it in Except(or other LINQ methods):
List<User> obsoleteUsers = olduser
.Except(newuser, new UserNameComparer(StringComparer.InvariantCultureIgnoreCase))
.ToList();
On this way you can implement multiple comparers for different requirements without changing the original class and the way it identifies duplicates(for example by the ID-property).
Note that Except(and other set based methods like Distinct) use GetHashCode to fast-check if an object equals another. That's why your class should override Equals and GetHashCode(always together) to support being used in a set based collection(like HashSet<T> or Dictionary<TKey, TValue>). Otherwise you will use the version from System.Object that just compares references and not properties.
If you want to compare for equality with your own rules, let's implement Equals and GetHashCode methods:
class User : IEquatable<User> {
// Dangerous practice: Id (and name) usually should be readonly:
// we can put instance into, say, dictionary and then change Id loosing the instance
public int Id { get; set; }
public string name { get; set; }
public bool Equals(User other) {
if (null == other)
return false;
return
Id == other.Id &&
string.Equals(name, other.name, StringComparison.OrdinalIgnoreCase);
}
public override bool Equals(object obj) => Equals(obj as User);
public override int GetHashCode() => Id;
}
Then you can put Except as usual
how can i compare 2 lists and have the not matching items but according to the specifics properties
public partial class Cable : StateObject
{
public int Id { get; set; }
public int CablePropertyId { get; set; }
public int Item { get; set; }
public int TagNo { get; set; }
public string GeneralFormat { get; set; }
public string EndString { get; set; }
public string CableRevision { get; set; }
}
I want to comparision accomplished accoring to the CablePropertyId,TagNo and CableRevision, if i use
var diffCables = sourceCables.Except(destinationCables).ToList();
the whole properties are compared to each other . how can i do that?
Use Linq except method with custom EqualityComparer.
http://msdn.microsoft.com/en-us/library/bb336390(v=vs.110).aspx
class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable x, Cable y)
{
return (x.CablePropertyId == y.CablePropertyId && ...);
}
public int GetHashCode(Cable x) // If you won't create a valid GetHashCode based on values you compare on, Linq won't work properly
{
unchecked
{
int hash = 17;
hash = hash * 23 + x.CablePropertyID;
hash = hash * 23 + ...
}
return hash;
}
}
var diffCables = sourceCables.Except(destinationCables, new CableComparer());
Also, ToList() operation on the result isn't really necessary. Most of the time you can just operate on the result of Linq query IEnumerable without specifying the exact type; this way you won't waste performance on unneeded ToList() operation.
By the way, a couple of others proposed Where-based queries with simple lambda. Such solution is easier to read (in my opinion), but it's also less optimized: it forces n^2 checks, while IEqualityComparer allows Linq to be more optimal because of GetHashCode() method. Here's a great answer on importance of GetHashCode, and here's a great guide on writing GetHashCode() override.
You can create your own IEqualityComparer<Cable> like this:
public class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable x, Cable y)
{
return x.CablePropertyId == y.CablePropertyId &&
x.TagNo == y.TagNo &&
x.CableRevision == y.CableRevision;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Cable x)
{
return x.CablePropertyId ^
x.TagNo.GetHashCode() ^
x.CableRevision.GetHashCode();
}
}
Then use this overload of Except:
var comparer = new CableComparer();
var diffCables = sourceCables.Except(destinationCables, comparer).ToList();
Alternatively, the MoreLINQ library (also available on NuGet) provides a convenient ExceptBy method:
var diffCables = sourceCables.ExceptBy(
destinationCables,
x => new {
x.CablePropertyId,
x.TagNo,
x.CableRevision
})
.ToList();
You can override the Equals and GetHashCode methods of Cable if you will always compare this object in this manner.
Otherwise you can write a custom comparer and use the overload for .Except
List.Except Method
I think you can use something like this:
sourceCables.Where(sc => !destinationCables.Any(dc => dc.CablePropertyId == sc.CablePropertyId && ...));
Essentially, when you want to compare your own types, you'll need to describe how they compare/differ from each other. Linq wouldn't know which properties in your Cable class are different right?
So you build a comparer which can be used generally to compare two types.
In this case, two Cable instances:
class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable c1, Cable c2)//these represent any two cables.
{
if (c1.Height == c2.Height && ...)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Cable c)
{
//this will work if each ID is unique
return c.Id.GetHashCode();
//otherwise you do this:
//return (c.Id ^ c. CablePropertyId).GetHashCode();
}
}
Then:
IEnumerable<Cable> except =
sourceCables.Except(destinationCables, new CableComparer());
If you use LINQ with IQueryable<>, there may be solution with Where()
var destinationCablesAnon = destinationCables.Select(a=>new {a.CablePropertyId, a.TagNo ,a.CableRevision}); // add ToArray() if use IEnumerable<>
var diffCables = sourceCables.Where(a=>!destinationCables.Contains(new {a.CablePropertyId, a.TagNo ,a.CableRevision})).ToList();
I want to get distinct list from list of persons .
List<Person> plst = cl.PersonList;
How to do this through LINQ. I want to store the result in List<Person>
Distinct() will give you distinct values - but unless you've overridden Equals / GetHashCode() you'll just get distinct references. For example, if you want two Person objects to be equal if their names are equal, you need to override Equals/GetHashCode to indicate that. (Ideally, implement IEquatable<Person> as well as just overriding Equals(object).)
You'll then need to call ToList() to get the results back as a List<Person>:
var distinct = plst.Distinct().ToList();
If you want to get distinct people by some specific property but that's not a suitable candidate for "natural" equality, you'll either need to use GroupBy like this:
var people = plst.GroupBy(p => p.Name)
.Select(g => g.First())
.ToList();
or use the DistinctBy method from MoreLINQ:
var people = plst.DistinctBy(p => p.Name).ToList();
Using the Distinct extension method will return an IEnumerable which you can then do a ToList() on:
List<Person> plst = cl.PersonList.Distinct().ToList();
You can use Distinct method, you will need to Implement IEquatable and override equals and hashcode.
public class Person : IEquatable<Person>
{
public string Name { get; set; }
public int Code { get; set; }
public bool Equals(Person other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
//Check whether the person' properties are equal.
return Code.Equals(other.Code) && Name.Equals(other.Name);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
int hashPersonName = Name == null ? 0 : Name.GetHashCode();
//Get hash code for the Code field.
int hashPersonCode = Code.GetHashCode();
//Calculate the hash code for the person.
return hashPersonName ^ hashPersonCode;
}
}
var distinctPersons = plst.Distinct().ToList();
In the code block below I would expect dictCars to contain:
{ Chevy:Camaro, Dodge:Charger }
But, dictCars comes back empty. Because this line returns false each time it's called:
if(myCars.Contains(new Car(Convert.ToInt64(strCar.Split(':')[1]),strCar.Split(':')[2])))
Code block:
public class Car
{
public long CarID { get; set; }
public string CarName { get; set; }
public Car(long CarID, string CarName)
{
this.CarID = CarID;
this.CarName = CarName;
}
}
List<Car> myCars = new List<Car>();
myCars.Add(new Car(0,"Pinto"));
myCars.Add(new Car(2,"Camaro"));
myCars.Add(new Car(3,"Charger"));
Dictionary<string, string> dictCars = new Dictionary<string, string>();
string strCars = "Ford:1:Mustang,Chevy:2:Camaro,Dodge:3:Charger";
String[] arrCars = strCars.Split(',');
foreach (string strCar in arrCars)
{
if(myCars.Contains(new Car(Convert.ToInt64(strCar.Split(':')[1]),strCar.Split(':')[2])))
{
if (!dictCars.ContainsKey(strCar.Split(':')[0]))
{
dictCars.Add(strCar.Split(':')[0], strCar.Split(':')[2]);
}
}
}
return dictCars;
Question: What am I doing wrong with my List.Contains implementation?
Thanks in advance!
You need to tell Contains what makes two Cars equal. By default it will use ReferenceEquals which will only call two objects equal if they are the same instance.
Either override Equals and GetHashCode in your Car class or define an IEqualityComparer<Car> class and pass that to Contains.
If two Cars that have the same CarID are "equal" then the implementation is pretty straightforward:
public override bool Equals(object o)
{
if(o.GetType() != typeof(Car))
return false;
return (this.CarID == ((Car)o).CarID);
}
public override int GetHashCode()
{
return CarID.GetHashCode();
}
Your Car class is a reference type. By default reference types are compared to each other by reference, meaning they are considered the same if they reference the same instance in memory. In your case you want them to be considered equal if they contain the same values.
To change the equality behavior, you need to override Equals and GetHashCode.
If two cars are equal only when ID and Name are equal, the following is one possible implementation of the equality members:
protected bool Equals(Car other)
{
return CarID == other.CarID && string.Equals(CarName, other.CarName);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
var other = obj as Car;
return other != null && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return (CarID.GetHashCode() * 397) ^
(CarName != null ? CarName.GetHashCode() : 0);
}
}
This implementation has been created automatically by ReSharper.
It takes into account null values and the possibility of sub-classes of Car. Additionally, it provides a useful implementation of GetHashCode.
You can add this code, by implementing IEquatable
public class Car: IEquatable<Car>
{
......
public bool Equals( Car other )
{
return this.CarID == other.CarID && this.CarName == other.CarName;
}
}
Link : http://msdn.microsoft.com/fr-fr/library/vstudio/ms131187.aspx
You are assuming that two Car instances that have the same CarID and CarName are equal.
This is incorrect. By default, each new Car(...) is different from each other car, since they are references to different objects.
There are a few ways to "fix" that:
Use a struct instead of a class for your Car.
Structs inherit ValueType's default implementation of Equals, which compares all fields and properties to determine equality.
Note that in this case, it is recommended that you make your Car struct immutable to avoid common problems with mutable structs.
Override Equals and GetHashCode.
That way, List.Contains will know that you intend Cars with the same ID and Name to be equal.
Use another method instead of List.Contains.
For example, Enumerable.Any allows you to specify a predicate that can be matched:
bool exists = myCars.Any(car => car.ID == Convert.ToInt64(strCar.Split(':')[1])
&& car.Name = strCar.Split(':')[2]);
You need to implement Equals. Most probably as:
public override bool Equals(object obj)
{
Car car = obj as Car;
if(car == null) return false;
return car.CarID == this.CarID && car.CarName == this.CarName;
}
Your car class needs to implement interface IEquatable and define an Equals method, otherwise the contains method is comparing the underlying references.
You need to implement the IEqualityComparer
More information on how to do it can be found here;
http://msdn.microsoft.com/en-us/library/bb339118.aspx
// Custom comparer for the class
class CarComparer : IEqualityComparer<Car>
{
// Products are equal if their names and product numbers are equal.
public bool Equals(Car x, Car y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the properties are equal.
return x.CarID == y.CarID && x.CarName == y.CarName;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Car car)
{
//Check whether the object is null
if (Object.ReferenceEquals(car, null)) return 0;
//Get hash code for the Name field if it is not null.
string hashCarName = car.CarName == null ? 0 : car.CarName.GetHashCode();
//Get hash code for the ID field.
int hashCarID = car.CarID.GetHashCode();
//Calculate the hash code for the product.
return hashCarName ^ hashCarID;
}
Check for equality;
CarComparer carComp = new CarComparer();
bool blnIsEqual = CarList1.Contains(CarList2, carComp);
A collection can never "contain" a newly newed object which uses the default Object.Equals comparison. (The default comparison is ReferenceEquals, which simply compares instances. This will never be true comparing an existing Car with a new Car())
To use Contains in this way, you will need to either:
Override Car.Equals (and Car.GetHashCode) to specify what it means to be equivalent, or
Implement an IEqualityComparer<Car> to compare the instances and specify that in your call to Contains.
Note the side effect that in the first option, other uses of Car.Equals(Car) will also use this comparison.
Otherwise, you can use Any and specify the comparison yourself (but IMHO this smells a little funny - a Car should know how to compare itself):
if(myCars.Any(c=> c.CarID == Convert.ToInt64(strCar.Split(':')[1]) && c.CarName == strCar.Split(':')[2]))
myCars.Contains(newCar)
myCars.Where(c => c.CarID == newCar.CarID && c.CarName==newCar.CarName).Count() > 0