public class A
{
public B obj{get; set;}
public A()
{
obj=new B();
}
}
public class B
{
public C obj{get; set;}
public B()
{
obj=new C();
}
}
public class C
{
public D obj{get; set;}
public C()
{
//obj=null;
obj=new D();
}
}
public class D
{
public string s{get;set;}
public D()
{
//s="Some string";
s=null;
}
}
public class Program
{
public static void Main(string[] args)
{
A testObject = new A();
if(testObject!=null && testObject.obj!=null && testObject.obj.obj!=null && testObject.obj.obj.obj!=null && testObject.obj.obj.obj.s !=null)
{
Console.WriteLine("String is not null");
return;
}
//This will obviously fails if any of the object of A,B,C,D class is null
//if(testObject.obj.obj.obj.s !=null)
//{
// Console.WriteLine("String is not null");
// return;
//}
Console.WriteLine("String is NULL");
return;
}
}
There exists 4 Classes (A,B,C,D) in Example having chained reference.
I just want to know if there exists better option to check for nullability then the one I used in this Example?
(I want to access string variable 's' from object of class A, so in between if any object is null, an exception is thrown. I want to know if there is better way as this will get worse if there are more deep levels of references , though they are not much recommended but what if it exists.)
http://rextester.com/CGUU62557
(PS:I read some answers , solving this by REFLECTION but that doesn't helps with generic collections.)
You can always use equals:
if(!testObject.equals(null))
{
//your logic
}
And a better way is to use ReferenceEquals:
if(Object.ReferenceEquals(null, testObject))
{
//your logic
}
The ReferenceEquals will return true if your object is null.
testObject!=null && testObject.obj!=null && testObject.obj.obj!=null && testObject.obj.obj.obj!=null etc.
can be rewritten as
testObject?.obj?.obj?.obj?.obj != null
But I would say to be referencing that many layers deep into an object and its properties has "code smell" warning attached to it. It means your code is coupled to the deep internals of your objects, which if they only represent plain data, may not be an issue, but if they represent complex behaviour is probably something you want to avoid.
Im not sure if this is what you want, but you can use the Null-Conditional Operator
if(A?.B?.C?.D == null)
{
//Something was null
}
Test it by making it null.
testObject.obj = null;
This will not throw any exception
if (testObject?.obj?.obj?.obj?.s != null)
This statement testObject?.obj?.obj?.obj?.s return null as a result
You can check it by using Reflection dynamically;
private static bool CheckPropertiesAsNullRecursively(object obj)
{
if (obj == null)
{
return true;
}
var objType = obj.GetType();
if (objType.IsPrimitive || objType == typeof(Decimal) || objType == typeof(String) || objType == typeof(Double))//You should compare the primitive types to determine them
{
return false;
}
var properties = objType.GetProperties();
foreach (var propertyInfo in properties)
{
if (CheckPropertiesAsNullRecursively(objType.GetProperty(propertyInfo.Name).GetValue(obj)))
{
return true;
}
}
return false;
}
Usage;
var val = CheckPropertiesAsNullRecursively(testObject);//If it returns true, at least one property should be null
Related
I am doing unit testing, and basically want to check that the data that 2 objects hold is the same
Assert.AreEqual(object1, object2);
Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
I am searching for the C# equivalent of assertJ
Assert.That(object1).isEqualToComparingFieldByField(object2)
You could either use records (c# 9 +) or you have to override the Equals method (if you have access and you can change the objects that you're working with).
Records example:
var point = new Point(3, 4);
var point2 = new Point(3, 4);
var test = point.Equals(point2); //this is true
public record Point(int X, int Y);
with classes:
public class Point
{
public int X { get; }
public int Y { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
return obj is Point point && (point.X == X && point.Y == Y);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
if you are not allowed to touch the implementation, then you could use serialization and compare the strings:
var obj1Str = JsonConvert.SerializeObject(object1);
var obj2Str = JsonConvert.SerializeObject(object2);
Assert.Equal(obj1Str, obj2Str);
using Newtonsoft.Json nuget
C# classes are reference equality, which means that variables are the same using the standard Equals and == if they point to the same object, you could override that behaivour, but it may break something now or in the future.
Or, you could switch to using a construct that's value equality by default, which structs as well as record classes are. If you can't (or don't want to) do that you can implement a value equals "helper" method yourself. I would not recommend overriding the Equals method or the == operator, as that can (and most likely will) lead to errors in the future instead I recommend you write your own ValueEquals method or extension method, something along the lines of
class Foo
{
public int Count {get; set;}
public string Message {get; set;}
}
public static bool ValueEquals(this Foo self, Foo other)
{
return self.Count == other.Count && self.Message == other.Message;
}
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(myFoo1.ValueEquals(myFoo2));
}
Depending on whether or not you can/ want to add a ValueEquals to your Foo class you can decide on doing it with an extension method or a normal method.
You could also implement a IEqualityComparer<T> like
public class FooValueEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo foo1, Foo foo2)
{
return foo1.Count == foo2.Count &&
foo1.Message == foo2.Message;
}
public int GetHashCode(Foo foo)
{
return foo.GetHashCode();
}
}
// Use it
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(new FooEqualityComparer().Equals(myFoo1, myFoo2));
}
Or, you could write a generic ValueEquals that works for all^* classes using Reflection:
public static class ValueEqualityComparer
{
public static bool ValueEquals<T>(this T self, T other) where T : class
{
var type = self.GetType();
if (type == typeof(string))
return self.Equals(other);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var selfValue = property.GetValue(self);
var otherValue = property.GetValue(other);
// String is special, it's not primitive but is value equality like primitives
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
{
if (!selfValue.Equals(otherValue))
return false;
}
// If the property is a List value equals each member
// Maybe find another type that allows indexing and is less restrictive
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var selfList = ((IEnumerable)property.GetValue(self)).Cast<object>();
var otherList = ((IEnumerable)property.GetValue(other)).Cast<object>();
try
{
// Using EquiZip from MoreLinq: https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/EquiZip.cs
foreach (var element in selfList.EquiZip(otherList, (selfItem, otherItem) => new { selfItem, otherItem }))
{
if (!ValueEquals(element.selfItem, element.otherItem))
return false;
}
}
catch (InvalidOperationException)
{
// MoreLINQ throws a InvalidOperationException if our two enumerables aren't the same length
return false;
}
}
else
{
if (!ValueEquals(selfValue, otherValue))
return false;
}
}
return true;
}
}
This implementation is by no means perfect, and should honestly only be used for UnitTests and also should be thoroughly tested itself. You can see my tests as a dotnetfiddle here
Or you could do it "dirty" and serialize the objects to a string and compare those values.
Using reflection, I'm attempting to find the set of types which inherit from a given base class. It didn't take long to figure out for simple types, but I'm stumped when it comes to generics.
For this piece of code, the first IsAssignableFrom returns true, but the second returns false. And yet, the final assignment compiles just fine.
class class1 { }
class class2 : class1 { }
class generic1<T> { }
class generic2<T> : generic1<T> { }
class Program
{
static void Main(string[] args)
{
Type c1 = typeof(class1);
Type c2 = typeof(class2);
Console.WriteLine("c1.IsAssignableFrom(c2): {0}", c1.IsAssignableFrom(c2));
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
generic1<class1> cc = new generic2<class1>();
}
}
So how do I determine at run time whether one generic type definition is derived from another?
From the answer to another question:
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
{
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
return true;
}
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return IsAssignableToGenericType(baseType, genericType);
}
The exact code you posted does not return surprising results.
This says "false":
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
This says "true":
Type g1 = typeof(generic1<class1>);
Type g2 = typeof(generic2<class1>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
The difference is that open generic types cannot have instances, so one is not "assignable" to the other.
From the docs:
Returns true if c and the current
Type represent the same type, or if
the current Type is in the
inheritance hierarchy of c, or if
the current Type is an interface
that c implements, or if c is a
generic type parameter and the current
Type represents one of the
constraints of c. false if none of
these conditions are true, or if c
is null.
In this case, clearly none of these conditions are true. And there's an extra note:
A generic type definition is not
assignable from a closed constructed
type. That is, you cannot assign the
closed constructed type
MyGenericList<int> (MyGenericList(Of Integer) in Visual Basic) to a
variable of type MyGenericList<T>.
In the following case use the method Konrad Rudolph provided could be wrong, like: IsAssignableToGenericType(typeof(A), typeof(A<>));// return false
I think here's a better answer
public static bool IsAssignableFrom(Type extendType, Type baseType)
{
while (!baseType.IsAssignableFrom(extendType))
{
if (extendType.Equals(typeof(object)))
{
return false;
}
if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
{
extendType = extendType.GetGenericTypeDefinition();
}
else
{
extendType = extendType.BaseType;
}
}
return true;
}
the test case, see Using IsAssignableFrom with C# generics for detail
using System;
/**
* Sam Sha - yCoder.com
*
* */
namespace Test2
{
class MainClass
{
public static void Main (string[] args)
{
string a = "ycoder";
Console.WriteLine(a is object);
A aa = new A();
//Console.WriteLine(aa is A<>);//con't write code like this
typeof(A<>).IsAssignableFrom(aa.GetType());//return false
Trace(typeof(object).IsAssignableFrom(typeof(string)));//true
Trace(typeof(A<>).IsAssignableFrom(typeof(A)));//false
AAA aaa = new AAA();
Trace("Use IsTypeOf:");
Trace(IsTypeOf(aaa, typeof(A<>)));
Trace(IsTypeOf(aaa, typeof(AA)));
Trace(IsTypeOf(aaa, typeof(AAA<>)));
Trace("Use IsAssignableFrom from stackoverflow - not right:");
Trace(IsAssignableFrom(typeof(A), typeof(A<>))); // error
Trace(IsAssignableFrom(typeof(AA), typeof(A<>)));
Trace(IsAssignableFrom(typeof(AAA), typeof(A<>)));
Trace("Use IsAssignableToGenericType:");
Trace(IsAssignableToGenericType(typeof(A), typeof(A<>)));
Trace(IsAssignableToGenericType(typeof(AA), typeof(A<>)));
Trace(IsAssignableToGenericType(typeof(AAA), typeof(A<>)));
}
static void Trace(object log){
Console.WriteLine(log);
}
public static bool IsTypeOf(Object o, Type baseType)
{
if (o == null || baseType == null)
{
return false;
}
bool result = baseType.IsInstanceOfType(o);
if (result)
{
return result;
}
return IsAssignableFrom(o.GetType(), baseType);
}
public static bool IsAssignableFrom(Type extendType, Type baseType)
{
while (!baseType.IsAssignableFrom(extendType))
{
if (extendType.Equals(typeof(object)))
{
return false;
}
if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
{
extendType = extendType.GetGenericTypeDefinition();
}
else
{
extendType = extendType.BaseType;
}
}
return true;
}
//from stackoverflow - not good enough
public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
if (it.IsGenericType)
if (it.GetGenericTypeDefinition() == genericType) return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return baseType.IsGenericType &&
baseType.GetGenericTypeDefinition() == genericType ||
IsAssignableToGenericType(baseType, genericType);
}
}
class A{}
class AA : A{}
class AAA : AA{}
}
I have a different Approach that resolves this issue, Here are my classes
public class Signal<T>{
protected string Id {get; set;} //This must be here, I use a property because MemberInfo is returned in an array via GetMember() reflection function
//Some Data and Logic And stuff that involves T
}
public class OnClick : Signal<string>{}
Now if I have an instance of type OnClick but I dont know that, and I want to find out if I have an instance of anything which inherits from Signal<> of any type? I do this
Type type = GetTypeWhomISuspectMightBeAGenericSignal();
PropertyInfo secretProperty = type.GetProperty("Id", BindingFlags.NonPublic | BindingFlags.Instance);
Type SpecificGenericType = secretProperty.DeclaringType; //This is the trick
bool IsMyTypeInheriting = SpecificGenericType.IsGenericType && SpecificGenericType.GetGenericTypeDefinition() == typeof(Signal<>); //This way we are getting the genericTypeDefinition and comparing it to any other genericTypeDefinition of the same argument length.
So this works for me, its not recursive, and it uses a trick via a designated property. It has limitations that its hard to write a function that checks assignability for all generics ever. But for a specific type it works
Obviously you need to check if() conditions better and stuff, but these are the Raw lines required to evaluate assignability of a type to its base generic, this way.
Hope this helps
My two cents. IMHO it doesn't make much sense to separate implements, derives or the original functionality of IsAssignableFrom,
Constructing from the answers previously given, this is how I do it:
public static bool ImplementsOrDerives(this Type #this, Type from)
{
if(from is null)
{
return false;
}
else if(!from.IsGenericType)
{
return from.IsAssignableFrom(#this);
}
else if(!from.IsGenericTypeDefinition)
{
return from.IsAssignableFrom(#this);
}
else if(from.IsInterface)
{
foreach(Type #interface in #this.GetInterfaces())
{
if(#interface.IsGenericType && #interface.GetGenericTypeDefinition() == from)
{
return true;
}
}
}
if(#this.IsGenericType && #this.GetGenericTypeDefinition() == from)
{
return true;
}
return #this.BaseType?.ImplementsOrDerives(from) ?? false;
}
You need to compare the contained type. See: How to get the type of T from a member of a generic class or method?
In other words, I think you need to check whether the type being contained by the generic class is assignable rather than the generic class itself.
#konrad_ruldolph's answer is mostly correct, but it requires you to know the base type/interface is an open generic. I propose an improvement that combines a non-generic test with a loop to test for generic match.
public static class Ext
{
public static bool IsAssignableToGeneric(
this Type assignableFrom,
Type assignableTo)
{
bool IsType(Type comparand)
=> assignableTo.IsAssignableFrom(comparand)
|| (comparand.IsGenericType
&& comparand.GetGenericTypeDefinition() == assignableTo);
while (assignableFrom != null)
{
if (IsType(assignableFrom)
|| assignableFrom
.GetInterfaces()
.Any(IsType))
{
return true;
}
assignableFrom = assignableFrom.BaseType;
}
return false;
}
}
Creating an extension method and using link you can do this :
public static bool IsAssignableFromGenericInterface(this Type type, Type genericInterface) => type.GetInterfaces().Any(#interface => #interface.IsAssignableFrom(genericInterface));
I also would like to share my code with you. Here the generic arguments are checked for any compatibility and is working with interfaces.
public static bool IsAssignableToGeneric(this Type sourceType, Type targetType)
{
bool IsAssignable(Type comperand)
{
if (comperand.IsAssignableTo(targetType))
return true;
if (comperand.IsGenericType && targetType.IsGenericType && comperand.GetGenericTypeDefinition() == targetType.GetGenericTypeDefinition())
{
for (int i = 0; i < targetType.GenericTypeArguments.Length; i++)
{
Type comperandArgument = comperand.GenericTypeArguments[i];
Type targetArgument = targetType.GenericTypeArguments[i];
// suggestion for improvement: forward the type check recursivley also here
if (!comperandArgument.IsGenericTypeParameter && !targetArgument.IsGenericTypeParameter && !comperandArgument.IsAssignableTo(targetArgument))
return false;
}
return true;
}
return false;
}
if (IsAssignable(sourceType))
return true;
if (targetType.IsInterface && sourceType.GetInterfaces().Any(IsAssignable))
return true;
return false;
}
I have an object lets call it ObjectA
and that object has 10 properties and those are all strings.
var myObject = new {Property1="",Property2="",Property3="",Property4="",...}
is there anyway to check to see whether all these properties are null or empty?
So any built-in method that would return true or false?
If any single of them is not null or empty then the return would be false. If all of them are empty it should return true.
The idea is I do not want to write 10 if statement to control if those properties are empty or null.
Thanks
You can do it using Reflection
bool IsAnyNullOrEmpty(object myObject)
{
foreach(PropertyInfo pi in myObject.GetType().GetProperties())
{
if(pi.PropertyType == typeof(string))
{
string value = (string)pi.GetValue(myObject);
if(string.IsNullOrEmpty(value))
{
return true;
}
}
}
return false;
}
Matthew Watson suggested an alternative using LINQ:
return myObject.GetType().GetProperties()
.Where(pi => pi.PropertyType == typeof(string))
.Select(pi => (string)pi.GetValue(myObject))
.Any(value => string.IsNullOrEmpty(value));
I suppose you want to make sure that all properties are filled in.
A better option is probably by putting this validation in the constructor of your class and throw exceptions if validation fails. That way you cannot create a class that is invalid; catch exceptions and handle them accordingly.
Fluent validation is a nice framework (http://fluentvalidation.codeplex.com) for doing the validation. Example:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Property1).NotNull();
RuleFor(customer => customer.Property2).NotNull();
RuleFor(customer => customer.Property3).NotNull();
}
}
public class Customer
{
public Customer(string property1, string property2, string property3)
{
Property1 = property1;
Property2 = property2;
Property3 = property3;
new CustomerValidator().ValidateAndThrow();
}
public string Property1 {get; set;}
public string Property2 {get; set;}
public string Property3 {get; set;}
}
Usage:
try
{
var customer = new Customer("string1", "string", null);
// logic here
} catch (ValidationException ex)
{
// A validation error occured
}
PS - Using reflection for this kind of thing just makes your code harder to read. Using validation as shown above makes it explicitly clear what your rules are; and you can easily extend them with other rules.
The following code returns if any property is not null.
return myObject.GetType()
.GetProperties() //get all properties on object
.Select(pi => pi.GetValue(myObject)) //get value for the property
.Any(value => value != null); // Check if one of the values is not null, if so it returns true.
Here you go
var instOfA = new ObjectA();
bool isAnyPropEmpty = instOfA.GetType().GetProperties()
.Where(p => p.GetValue(instOfA) is string) // selecting only string props
.Any(p => string.IsNullOrWhiteSpace((p.GetValue(instOfA) as string)));
and here's the class
class ObjectA
{
public string A { get; set; }
public string B { get; set; }
}
A slightly different way of expressing the linq to see if all string properties of an object are non null and non empty:
public static bool AllStringPropertyValuesAreNonEmpty(object myObject)
{
var allStringPropertyValues =
from property in myObject.GetType().GetProperties()
where property.PropertyType == typeof(string) && property.CanRead
select (string) property.GetValue(myObject);
return allStringPropertyValues.All(value => !string.IsNullOrEmpty(value));
}
Note if you've got a data structural hierarchy and you want to test everything in that hierarchy, then you can use a recursive method. Here's a quick example:
static bool AnyNullOrEmpty(object obj) {
return obj == null
|| obj.ToString() == ""
|| obj.GetType().GetProperties().Any(prop => AnyNullOrEmpty(prop.GetValue(obj)));
}
To only check if all properties are null:
bool allPropertiesNull = !myObject.GetType().GetProperties().Any(prop => prop == null);
you can use reflection and extension methods to do this.
using System.Reflection;
public static class ExtensionMethods
{
public static bool StringPropertiesEmpty(this object value)
{
foreach (PropertyInfo objProp in value.GetType().GetProperties())
{
if (objProp.CanRead)
{
object val = objProp.GetValue(value, null);
if (val.GetType() == typeof(string))
{
if (val == "" || val == null)
{
return true;
}
}
}
}
return false;
}
}
then use it on any object with string properties
test obj = new test();
if (obj.StringPropertiesEmpty() == true)
{
// some of these string properties are empty or null
}
No, I don't think there is a method to do exactly that.
You'd be best writing a simple method that takes your object and returns true or false.
Alternatively, if the properties are all the same, and you just want to parse through them and find a single null or empty, perhaps some sort of collection of strings would work for you?
You can try the following query :
if the object is "referenceKey" (where few properties may be null )
referenceKey.GetType().GetProperties().Where(x => x.GetValue(referenceKey) == null)
I need to count the properties where the value is set to not Null, so I have used the following query :
var countProvidedReferenceKeys = referenceKey.GetType().GetProperties().Where(x => x.GetValue(referenceKey) != null).Count();
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
What is the best way to compare two entity framework entities?
I want to know the most efficient way of comparing two entities of the same type.
One entity is created from an xml file by hand ( ie new instance and manually set properties) and the other is retvied from my object context.
I want to know if the property values are the same in each instance.
My first thoughts are to generate a hash of the property values from each object and compare the hashes, but there might be another way, or a built in way?
Any suggestions would be welcome.
Many thanks,
James
UPDATE
I came up with this:
static class ObjectComparator<T>
{
static bool CompareProperties(T newObject, T oldObject)
{
if (newObject.GetType().GetProperties().Length != oldObject.GetType().GetProperties().Length)
{
return false;
}
else
{
var oldProperties = oldObject.GetType().GetProperties();
foreach (PropertyInfo newProperty in newObject.GetType().GetProperties())
{
try
{
PropertyInfo oldProperty = oldProperties.Single<PropertyInfo>(pi => pi.Name == newProperty.Name);
if (newProperty.GetValue(newObject, null) != oldProperty.GetValue(oldObject, null))
{
return false;
}
}
catch
{
return false;
}
}
return true;
}
}
}
I haven't tested it yet, it is more of a food for thought to generate some more ideas from the group.
One thing that might be a problem is comparing properties that have entity values themselves, if the default comparator compares on object reference then it will never be true. A possible fix is to overload the equality operator on my entities so that it compares on entity ID.
As is the code will not do what you are expecting.
Try this simple test:
class A {
public int Id { get; set; }
public string Name { get; set; }
}
class B : A {
public DateTime BirthDate { get; set; }
}
class ObjectComparer {
public static void Show() {
A a = new A();
B b = new B();
A a1 = new A();
Console.WriteLine(ObjectComparator.CompareProperties(a, b));
Console.WriteLine(ObjectComparator.CompareProperties(b, a));
Console.WriteLine(ObjectComparator.CompareProperties(a, a1));
}
}
You would expect it to return
false
false
true
but it returns
false
false
false
try changing the inner if to look like:
if (!object.Equals(newProperty.GetValue(newObject, null), oldProperty.GetValue(oldObject, null))) {
return false;
}
You can also save some time in the case a and a1 both reference the same object by checking that in the begining of the method.
static class ObjectComparator {
public static bool CompareProperties(T newObject, T oldObject) {
if (object.Equals(newObject, oldObject)) {
return true;
}
if (newObject.GetType().GetProperties().Length != oldObject.GetType().GetProperties().Length) {
return false;
}
else {
var oldProperties = oldObject.GetType().GetProperties();
foreach (PropertyInfo newProperty in newObject.GetType().GetProperties()) {
try {
PropertyInfo oldProperty = oldProperties.Single(pi => pi.Name == newProperty.Name);
if (!object.Equals(newProperty.GetValue(newObject, null), oldProperty.GetValue(oldObject, null))) {
return false;
}
}
catch {
return false;
}
}
return true;
}
}
}
If you are concered with performance, you can cache the return of Type.GetProperties into a local variable during the lifetime of the method, since Reflection does not do that by itself at least up to version 3.5 SP1. In doing that you will drop GetProperties calls from four to two.
If you are only expecting to compare objects of exactly the same type (or put another way not compare between base and derived instances), you can further reduce the calls of GetProperties to one.
Hope this helps.
I would do something like this
static class ObjectComparator<T>
{
public static bool CompareProperties(T newObject, T oldObject)
{
if (Equals(newObject, oldObject))
{
return true;
}
PropertyInfo[] newProps = newObject.GetType().GetProperties();
PropertyInfo[] oldProps = oldObject.GetType().GetProperties();
if (newProps.Length != oldProps.Length)
{
return false;
}
foreach (PropertyInfo newProperty in newProps)
{
PropertyInfo oldProperty = oldProps.SingleOrDefault(pi => pi.Name == newProperty.Name);
if (oldProperty == null)
return false;
object newval = newProperty.GetValue(newObject, null);
object oldval = oldProperty.GetValue(oldObject, null);
if (!Equals(newval, oldval))
return false;
}
return true;
}
}
You will get a null reference exception (on the next line) if the line that gets oldproperty cannot find a property with the correct name.
I've defined a C# class with a string member. For all intents an purposes, think of this class as being a subclass of string (except that's not allowed). I'm using it to represent a strongly typed string field that matches a specific format (I've simplified this significantly).
public class field
{
private readonly string m_field;
public field(string init_value)
{
//Check the syntax for errors
if (CheckSyntax(init_value))
{
m_field = init_value;
}
else
{
throw new ArgumentOutOfRangeException();
}
}
public override string ToString()
{
return m_field;
}
}
Now, I want to be able to compare this class directly to any other string (object or literal). Therefore, I implemented the following in the class:
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
return this.m_field == obj.ToString();
}
public override int GetHashCode()
{
return this.m_field.GetHashCode();
}
public static bool operator ==(field x, Object y)
{
if ((object)x == null && y == null)
{
return true;
}
else if ((object)x == null || y == null)
{
return false;
}
else
{
return (x.m_field == y.ToString());
}
}
public static bool operator !=(field x, Object y)
{
return !(x == y);
}
Now when I'm writing a unit test, depending on the order that I'm passing in the arguments to Assert.AreEqual, I get different results:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // PASSES
Assert.AreEqual(valid, target); // FAILS
I'm assuming this is because in the first assert, it's calling field.Equals() and in the second it's calling String.Equals(). Obviously I'm approaching this from the wrong angle. Can anyone give me some insight?
One other thing. I can't use a struct here (value type) because in my actual case I'm defining all this in a base class and inheriting from it.
Basically you can't do what you want to - there's no way you can make string recognise your class for equality purposes. You'll never be able to make it reflexive - you'll never be able to make it obey the contract of object.Equals.
I would personally try to redesign it so that you didn't have the validation as part of the type itself - make it part of the relevant properties of the business entities (or whatever they are).
This is described in detail in Effective Java as Item 8: Obey the general contract when overriding equals.
The equals method implements an equivalence relation.
It is Reflexive, Symmetric, Transitive, Consistent, and for any non-null reference x, x.equals(null) must return false. The example cited to break symmetry is similar to yours.
field class is aware of string class, but the built-in string class is not aware of field. This a one-way interoperability, and should be removed.
I would discourage anyone using your field class implicitly as a String, and force this type of usage:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.toString(), valid);
Assert.AreEqual(valid, target.toString());
Based on everyone's feedback, and my own needs, here's what I'm putting forward as a possible solution (I'm modifying the Equals method as follows):
public override bool Equals(Object obj)
{
if (obj == null)
{
return false;
}
field f = obj as field;
if (f != null)
{
return this == f;
}
else
{
return obj.Equals(this);
}
}
This seems to allow its correct use in dictionary and collection classes that rely on the Equals and GetHashCode methods for determining if the value already exists.
Also, now these both fail:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // FAILS
Assert.AreEqual(valid, target); // FAILS
And these both pass:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.ToString(), valid); // PASSES
Assert.AreEqual(valid, target.ToString()); // PASSES
And these both pass:
field f1 = new field("Some String");
field f2 = new field("Some String");
Assert.AreEqual(f1, f2); // PASSES
Assert.AreEqual(f2, f1); // PASSES
This is String#Equals
public override bool Equals(object obj)
{
string strB = obj as string;
if ((strB == null) && (this != null))
{
return false;
}
return EqualsHelper(this, strB);
}
Supplying an argument other than a String to String#Equals is going to return false. I'd suggest a 'rethink' to get around this.
I suggest using object.ReferenceEquals() if you are internally trying to validate whether x or y is null.
public static bool operator ==(field x, Object y)
{
if (object.ReferenceEquals(x, null) && object.ReferenceEquals(y, null))
{
return true;
}
else if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
{
return false;
}
else
{
return (x.m_field == y.ToString());
}
}