Comparing the value of a property in two instances - c#

I think I'm having boxing issues
foreach(var p in item.GetType().GetProperties().
Where(p => p.GetValue(original, null) is ValueType))
{
var originalValue = p.GetValue(original, null);
var modifiedValue = p.GetValue(item, null);
if (!originalValue.Equals(modifiedValue))
kvpData.AppendFormat("{0}={1}&", p.Name, originalValue);
}
originalValue is never equal to modifiedValue, Im guessing it's because they are boxed inside Object. But how do I fix it?

It is not a boxing issue. Equals is a virtual method, which the boxed value types override just fine.
However, I am not sure what the issue is. Could it be that there aren’t actually any matching properties? Remember that GetProperties() without any parameters will only return public properties. If the properties you need are private, you need to add some BindingFlags:
GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
(I’m assuming here that you don’t want static properties.)
Are you also sure that it is actually properties that you are after and not fields? Remember that if you declare something as
public string Name;
then it’s a field, whereas
public string Name { get; set; }
is a property. If it’s actually fields you need, you need to use GetFields() instead of GetProperties() with the same binding flags.

Linq is a great tool, but I'm not sure why you're using it here. You're actually causing the set of properties to get iterated twice, which is very expensive. I'd write the code without Linq. Also, there's no need to get the value more than once, which is again very expensive. Give this code a try. It avoids the flaws I pointed out and was comparing correctly when I made and tested a dummy class with it:
foreach(PropertyInfo p in item.GetType().GetProperties())
{
if (p.PropertyType.BaseType == typeof(ValueType) || p.PropertyType == typeof(string))
{
var originalValue = p.GetValue(original, null);
var modifiedValue = p.GetValue(item, null);
if (originalValue != modifiedValue) kvpData.AppendFormat("{0}={1}&", p.Name, originalValue);
}
}
Also, note that strings are not a ValueType although they do implement a value comparison.

From MSDN: Object.Equals :
The default implementation of Equals supports reference equality for
reference types, and bitwise equality for value types. Reference
equality means the object references that are compared refer to the
same object. Bitwise equality means the objects that are compared have
the same binary representation.
That means that in your case this 2 objects (if they are reference types) are never point to the same instance.
There is no an easy way to resolve this in so generic way as you would like to do.
But you can implement (just an example) an IComparable on those types you gonna to compare, and after in this iteration check if the type of the value rturned implements that interface, so cast and call it's implemented IComparable.CompareTo method.
You can check if the object implements specified interfacce, in this current case IComparable, you can do something like this:
originalValue .GetType().GetInterfaces().Any(x =>
x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IComparable))
Hope this helps.

Related

Compare property values with Reflection

I am trying to build the most budget of ORMs.
I have 2 objects, one "item" and one "original", when creating item, i simply save a COPY of it, in a variable called original. The copy is created via an extension i stole from this post https://stackoverflow.com/a/11308879/10647851
So i want to build my update query, very simple i thought.
foreach (PropertyInfo prop in properties) {
var one = prop.GetValue(original, null);
var typeOne = one.GetType();
var two = prop.GetValue(item, null);
var typeTwo = two.GetType();
if(one != two) { ... }
}
However, one is unequal to two in several cases where you would not expect it.
The ID (type in the model = int?) 2703 is unequal to 2703. I stepped through the code and the type is Int32. Booleans are problems too. Strings work. DateTime?'s work.
Use
if(!object.Equals(one, two)) { ... }
for equality comparison instead. That will invoke the default equality comparator or the overridden one for those types that have implemented it (which includes all value types like int on your example).
!= uses reference equality and therefore produces the same value when the referenced types are exactly the same instance. Using != will give you unexpected results due to the fact you are using GetValue which returns an object.
public object GetValue(object obj)
because one and two are actually reference types (i.e. they are boxed ints instead of value types) the "==" comparator performs a reference equality check.

Picking objects of a given type from a List

I have a Type object. I want to find every object in a List that is of this type. Is there something like list.OfType<> using a Type object? Or do I have to use it this way:
list.Where(obj => obj.GetType() == type) // corrected thanks to jmcilhinney
This will be heavily used in my application, so I need to find a solution that is as fast as possible.
You need to call Where:
var items = myList.Where(item => item.GetType() == myType);
You can't use OfType<T> in the case where you have the type stored in a variable. Remember that, with generics, the type parameter is a compile-time constant. Therefore you can't use a generic function by specifying T in a variable, which is not known until runtime.
To look at this another way, there are two possible variants of your function:
public IEnumerable<T> GetOfType<T>(IEnumerable<object> list)
{
return list.OfType<T>();
}
public IEnumerable<object> GetOfType(IEnumerable<object> list, Type type)
{
return list.All(obj => obj != null && obj.GetType() == type);
}
To use the generic version, ultimately some consumer of the function is going to need to supply the type as a compile-time constant.

Writing a certain generic method

I have a series of methods to write, but I think there is some commonality (well, I know there is). The method I'd like to see will take two things, an object and... well, I'm not so sure about the second one, but probably a string.
The object should be generic, although it can only be from a set list (in this case, that commonality seems to be that they inherit from both INotifyPropertyChanging and INotifyPropertyChanged interfaces).
The string should be the name of a property within the generic object. It should be checked to see if that property exists within that object before being put into use (it would be used as a way to compare the objects by that given property).
So I guess the process would be... generic object gets passed into method (along with property name string). A check to see if the object contains the property. If it does, continue and somehow have access to 'object.PropertyName' where 'PropertyName' was the supplied string.
I don't know if it's easy or possible or even wise, but I know that it would save me some time.
Thanks in advance for any advice you might be able to offer with this.
Edit: Thanks for the all the replies so far guys. Let me clarify some things:
Clarification of 'access'
When I said, "... and somehow have access to 'object.PropertyName'", what I meant was that the method should be able to use that property name as if it were just a property of that object. So, let's say the string passed in was "ClientName", there would be the ability to read (possibly write, although at the moment I don't think so as it's just a check) object.ClientName, if it was determined that existed.
What I'm trying to do
I have a WCF service which accesses an SQL database using Linq. The objects I spoke of are entities, generated from the program SQLMetal.exe, so my objects are things like 'Client', 'User' and this sort of thing. I wrote a method which took a List of entities. This method added only those entities which did not exist within the collection (some could have been duplicates). It figured out which ones were duplicates by checking a property within the entity (which corresponds to data in a column of the database). It's that property which I figured might be variable.
It sounds like you don't really want to check if it's a certain type, and if that is so then you don't have to and its actually easier not to check the type. This shows how to check if the property exists and if it is readable and writeable and shows how to use it after it's found:
private void Form1_Load(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
PropertyInfo info = GetProperty(sb, "Capacity");
//To get the value of the property, call GetValue on the PropertyInfo with the object and null parameters:
info.GetValue(sb, null);
//To set the value of the property, call SetValue on the PropertyInfo with the object, value, and null parameters:
info.SetValue(sb, 20, null);
}
private PropertyInfo GetProperty(object obj, string property)
{
PropertyInfo info = obj.GetType().GetProperty(property);
if (info != null && info.CanRead && info.CanWrite)
return info;
return null;
}
I think only indexer properties can take parameters in C#. And I believe if you wrote properties in VB that take parameters and tried to reference that assembly in C# they would show up as methods instead of properties.
You could also write a function like this that would take 2 objects and a string for a property name and return the result of those properties matching:
private bool DoObjectsMatch(object obj1, object obj2, string propetyName)
{
PropertyInfo info1 = obj1.GetType().GetProperty(propertyName);
PropertyInfo info2 = obj2.GetType().GetProperty(propertyName);
if (info1 != null && info1.CanRead && info2 != null && info2.CanRead)
return info1.GetValue(obj1, null).ToString() == info2.GetValue(obj2, null).ToString();
return false;
}
Comparing the values of the properties might be tricky because it would compare them as objects and who knows how equality will be handled for them. But converting the values to strings should work for you in this case.
If you know the 2 objects are the same type then you can simplify it:
private bool DoObjectsMatch(object obj1, object obj2, string propetyName)
{
PropertyInfo info = obj1.GetType().GetProperty(propertyName);
if (info != null && info.CanRead)
return info.GetValue(obj1, null).ToString() == info.GetValue(obj2, null).ToString();
return false;
}
I think you're looking for something like:
public void SomeMethod<T>(T object, string propName)
where T : INotifyPropertyChanging, INotifyPropertyChanged
(
var type = typeof(T);
var property = type.GetProperty(propName);
if(property == null)
throw new ArgumentException("Property doesn't exist", "propName");
var value = property.GetValue(object, null);
)

Extract a generic value using reflection

I have the following interface in my project.
public interface Setting<T>
{
T Value { get; set; }
}
Using reflection, I would like to examine properties that implement this interface and extract Value.
So far I have written this, which successfully creates a list of properties that implement Setting.
var properties = from p in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
where p.PropertyType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IAplSetting<>))
select p;
Next I would like to do this: (Please ignore the undefined variables, you can assume that they really do exist in my actual code.)
foreach (var property in properties)
{
dictionary.Add(property.Name, property.GetValue(_theObject, null).Value);
}
The problem is that GetValue returns an object. In order to access Value, I need to be able to cast to Setting<T>. How would I get Value and store it, without needing to know the exact type of T?
You could continue this approach with one more level of indirection:
object settingsObject = property.GetValue(_theObject, null);
dictionary.Add(
property.Name,
settingsObject.GetType().GetProperty("Value")
.GetValue(settingsObject, null));
If you're using dynamic (re: your comment about ExpandoObject), this can be much simpler:
dynamic settingsObject = property.GetValue(_theObject, null);
dictionary.Add(property.Name, settingsObject.Value);

Why is the == operator (defined for the concrete type) not used?

I have a list defined as:
var Items = new List<IItem>();
Now there are a number of different classes that have that interface and one of them is Consumable. The Consumable class also has the == operator overloaded. Now I have the following code and is not working:
if(item1 == item2)
{
//code...
}
This does not work. I put a break point in the == operator overload and it never gets to it. When I go through line-by-line debugging, both item1 and item2 are of type Consumable, both GetType returns Consumable. I even try this code:
var temp = item1.GetType();
var temp2 = item2.GetType();
if (temp == temp2)
{
//code...
}
and this equality results is true. Now if I try this:
if(((Consumable)item1) == ((Consumable)item2))
{
//code...
}
and this triggers the break point in the == operator overload. Why would I have to manually cast the variable if when line-by-line debugging show it already thinks they are both consumable? Is it because I am pulling them from a list of IItems?
Since your list is List<IItem>, I'm assuming you have something like:
var item1 = Items[0];
or whatever; here item1 the variable is typed as IItem. Operator resolution happens during build via static analysis (not at runtime via polymorphism/RTTI), so the only == available is the default for any object, i.e. reference equality.
To support your custom operator, the variables must be typed accordingly, for example:
Consumable item1 = ..., item2 = ...;
Your cast achieves a similar thing.
Another option would be to make sure that == and Equals (and GetHashCode()) are in agreement, and use:
if(Equals(item1, item2)) {...}
which will do null checks and then use your overridden Equals method. This then supports polymorphism, so it doesn't matter what the types are.
The common langauge runtime does only know that your two objects implement the interface IItem. The smallest common part in object hierarchy is System.Object. And you did not overload the == operator of System.Object.
To use the correct overload you have to state the type of the object.
== does not check type equality, but for classes it checks reference equality. So if the variables are pointing to the same object it will be true. For example like this:
var temp = item1;
var temp2 = item1;
if( temp == temp2 )
{
//this code will execute
}
Shouldn't be the IItem IComparable?

Categories