I have recently written a simple method on a class that does a DeepCopy of all properties and returns the new property.
Below are three sample classes and the DeepCopy method:
class Person
{
public int Age {get; set;}
public string Name {get; set;}
Public Address address {get; set;}
public List<Person> Children {get; set;}
}
class Address
{
public string StreetAddress {get; set;}
public int ZipCode {get; set; }
public Region region {get; set;}
}
class Region
{
public string City {get; set;}
public string Country {get; set;}
}
public static Person DeepCopy(this Person p)
{
return new Person
{
Age = p.Age,
Name = p.Name,
Address = p.Address,
Children = p.Children
}
}
I want to write unit tests, to test:
All the properties of the source object are copied to the second object and the values are identical.
Using reflection, get the list of the properties recursively (since each object can have a property which is another type which itself has a property...) and make sure the DeepCopy method copies all the properties. The reason for this test is to fail, if a new property is added to the Person class and not copied to the new object in DeepCopy method.
I have already tried using reflection to get all the properties, but one of the problems I have is at some point, it gets stuck in a loop.
For unit tests there is lib FluentAssetions:
string username = "dennis";
username.Should().Be("jonas");
Expected username to be "jonas", but "dennis" differs near 'd' (index 0).
As #Backs said, FluentAssertions is what you need here:
To assert that two objects are equal (through their implementation of Object.Equals), use
string otherObject = "whatever";
theObject.Should().Be(otherObject, "because they have the same values");
theObject.Should().NotBe(otherObject);
Try this:
// Taken from: https://gist.github.com/jonathanconway/3330614
public static bool IsSimpleType(this Type type)
{
return
type.IsValueType ||
type.IsPrimitive ||
new Type[] {
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
Convert.GetTypeCode(type) != TypeCode.Object;
}
public static bool CompareIEnumerable(IEnumerable enumerable1, IEnumerable enumerable2)
{
var obj1Iterator = enumerable1.GetEnumerator();
var obj2Iterator = enumerable2.GetEnumerator();
bool has1 = obj1Iterator.MoveNext(), has2 = obj2Iterator.MoveNext();
//loop through the enumerables
while (has1 && has2)
{
//compare the values deeply
if (!DeepCompare(obj1Iterator.Current, obj2Iterator.Current))
{
return false;
}
has1 = obj1Iterator.MoveNext();
has2 = obj2Iterator.MoveNext();
}
// if the loop terminated and has1 != has2, one of them have more items, the are not equal
return has1 == has2;
}
public static bool DeepEquals<T>(this T obj1, T obj2)
{
//if one is null and the other is not, they are not equal
if (obj1 != null ^ obj2 != null)
{
return false;
}
//else if both are null, they are equal
else if (obj1 == null && obj2 == null)
{
return true;
}
Type objectsType = obj1.GetType();
//if they are a simple type, compare them using .Equals method
if (objectsType.IsSimpleType())
{
return obj1.Equals(obj2);
}
//if they are IEnumerable type, compare them using CompareIEnumerable method
else if (objectsType.GetInterface("IEnumerable") != null)
{
return CompareIEnumerable((IEnumerable)obj1, (IEnumerable)obj2);
}
//The type is not simple nor IEnumerable, loop through the properties and check if they are equal (Deeply)
foreach (var member in objectsType.GetMembers().Where(m => m.MemberType.Equals(MemberTypes.Field) || m.MemberType.Equals(MemberTypes.Property)))
{
Type t = member.MemberType.Equals(MemberTypes.Field) ?
((FieldInfo)member).FieldType :
((PropertyInfo)member).PropertyType;
Func<object, object> getter = member.MemberType.Equals(MemberTypes.Field) ?
new Func<object, object>(((FieldInfo)member).GetValue) :
new Func<object, object>(((PropertyInfo)member).GetValue);
if (!DeepCompare(getter(obj1), getter(obj2)))
{
return false;
}
}
return true;
}
Related
I have found some direction for this problem but have not found anything which I can apply to this problem.
I want to filter lists of different types by stated properties they hold. I can use linq to dynamically filter a List by Test.id but I cant manage to filter a List through MyClass.Name
I have these classes.
public class Test
{
public int Id { get; set; }
public MyClass myclass { get; set; }
}
public class MyClass
{
public string Name { get; set; }
}
This is what I'm trying to do.
static void Main(string[] args)
{
var source = new List<Test> {
new Test { Id = 1,myclass = new MyClass() { Name = "bob" } },
new Test { Id = 2,myclass= new MyClass() { Name = "joe" } } };
var x = myFilter(source,"Name", "bob");
Console.WriteLine(x.Count());
}
public static IEnumerable<T> myFilter<T>(List<T> source, string propertyName, string searchString)
{
// get the myclass property then the stated property(Name) value within it
searchString = searchString.ToLower();
return source.Where(s => (s.GetType().GetProperty("myclass")
.GetType().GetProperty(propertyName)
.GetValue(s.GetType().GetProperty("myclass"),null).ToString() ?? " ")
.ToLower().Contains(searchString));
}
The count return 0 when I am expecting 1. for Test.MyClass.Name = "bob"
Is there a solution for this or is there a better way to do it besides reflection?
Thanks
you need to use the PropertyType of the returned myclass property:
public static IEnumerable<T> myFilter<T>(List<T> source, string propertyName, string searchString)
{
// get the myclass property then the stated property(Name) value within it
searchString = searchString.ToLower();
return source.Where(s => (s.GetType().GetProperty("myclass")
.PropertyType.GetProperty(propertyName)
.GetValue(s.GetType().GetProperty("myclass").GetValue(s)).ToString() ?? " ")
.ToLower().Contains(searchString));
}
You should be able to use the following:
var count = source.Count(test =>
string.Compare(test.myClass.Name, "Bob",
StringComparison.CurrentCultureIgnoreCase) == 0);
This will compare the string value of the Name Property and only count where the name is equal to "bob" and it will ignore the case.
If you want to return the Test object instead then you can use the following
var results = source.Where(test =>
string.Compare(test.myClass.Name, "Bob",
StringComparison.CurrentCultureIgnoreCase) == 0);
Objective: process an object and if the object implements an expected type, I want to change a specific property value (this part is working fine), and I also would like to apply the same logic to all property lists (that I explicit point) that are of the same expected type.
I have the following code:
public abstract class BaseObject
{
public int Id { get; set; }
}
public class Father : BaseObject
{
public DateTime CreatedOn { get; set; }
public string Name { get; set; }
public IEnumerable<ChildA> Children1 { get; set; }
public IEnumerable<ChildB> Children2 { get; set; }
public IEnumerable<ChildA> Children3 { get; set; }
public IEnumerable<ChildB> Children4 { get; set; }
}
public class ChildA : BaseObject
{
public int Val1 { get; set; }
}
public class ChildB : BaseObject
{
public string Name { get; set; }
public int Total { get; set; }
}
I want to process an object by applying some changes on a specific property on the target object and on all property children that I explicit say:
public void Start()
{
var listA = new List<ChildA> { new ChildA { Id = 1, Val1 = 1 }, new ChildA { Id = 2, Val1 = 2 } };
var listB = new List<ChildB> { new ChildB { Id = 1, Name = "1", Total = 1 } };
var obj = new Father { Id = 1, CreatedOn = DateTime.Now, Name = "F1", ChildrenA = listA, ChildrenB = listB };
// I explicit tell to process only 2 of the 4 lists....
ProcessObj(obj, x => new object[] { x.Children1, x.Children2 });
}
I was able to write this function:
public void ProcessObj<T>(T obj, Expression<Func<T, object[]>> includes = null)
{
var objBaseObject = obj as BaseObject;
if (objBaseObject == null) return;
// Here I change the ID - add 100 just as an example....
objBaseObject.Id = objBaseObject.Id + 100;
if (includes == null) return;
var array = includes.Body as NewArrayExpression;
if (array == null) return;
var exps = ((IEnumerable<object>)array.Expressions).ToArray();
for (var i = 0; i < exps.Count(); i++)
{
var name = ((MemberExpression)exps[i]).Member.Name;
var childProperty = obj.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance
).FirstOrDefault(prop => prop.Name == name);
if (childProperty == null) continue;
// NOT correct because I think I am getting a copy of the object
// and not pointing to the object in memory (by reference)
var childList = childProperty.GetValue(obj);
// TODO: loop on the list and apply the same logic as the father....
// change the ID field....
}
}
In this prototype I started writing reflection, but I really would like to avoid it if possible....
How can I do this???
Maybe I'm missing something, but it seems like you're complicating the problem by using expression trees. Can you just not use a regular Action and Func delegates to do this? Why do they need to be expression trees? Here's an example just using delegates:
public void ProcessObj<T>(T obj, Func<T, IEnumerable<object>> includes) {
var objBaseObject = obj as BaseObject;
if (objBaseObject == null) return;
// Create a reusable action to use on both the parent and the children
Action<BaseObject> action = x => x.Id += 100;
// Run the action against the root object
action(objBaseObject);
// Get the includes by just invoking the delegate. No need for trees.
var includes = includes(obj);
// Loop over each item in each collection. If the types then invoke the same action that we used on the root.
foreach(IEnumerable<object> include in includes)
{
foreach(object item in include)
{
var childBaseObject = item as BaseObject;
if(childBaseObject != null)
{
action(childBaseObject);
}
}
}
}
Useable just like before:
ProcessObj(obj, x => new object[] { x.Children1, x.Children2 });
No expression trees and no reflection, just regular delegate lambdas.
Hope that helps
say there is a class like
class phones
{
public int Id {get; set;}
public string Name {get; set;}
public string Color {get; set;}
public decimal Price {get; set;}
}
List<phones> myList = GetData();
//list is filled with objects
Now, I know the Id and the exact name of the object's property and want to get the value from the matching object.
private string GetValue(int pid, string featurename)
{
string val = "";
foreach(phones obj in myList)
{
if(obj.Id == pid)
{
//if featurename is 'Name', it should be
//val = obj.Name;
//if featurename is 'Price', it should return
//val = obj.Price;
break;
}
}
return val;
}
Is this possible. Please advise.
Use this:
Phones phones= new Phones();
string returnValue = phones.GetType().GetProperty(featureName).GetValue(phones, null).ToString();
Also, remember to add validation for input featureName and error handling.
How about this:
foreach(phones obj in myList)
{
if(obj.Id == pid)
{
if (featurename == "Name")
{
return obj.Name;
}
else if (featurename == "Price")
{
return obj.Price.ToString();
}
else
{
return string.Empty;
}
}
}
I think you want Property with given featurename and use it
you could use lambda expression like this
or
Use PropertyInfo like this
foreach (PropertyInfo p in typeof(ClassName).GetProperties())
{
string propertyName = p.Name;
//....
}
Existing code (simplified)
I have this function
public static string[] GetFieldNames<T>(IEnumerable<T> items)
where T : class
{
var properties = typeof(T).GetProperties().Where(p => SystemTypes.Contains(p.PropertyType)); // Only get System types
return properties.Select(p => p.Name).ToArray();
}
So if say I have this class
class MyClass {
public string Name { get; set; }
[Description("The value")]
public int Value { get; set; }
}
I can have code like this
List<MyClass> items = ...; // Populate items somehow
string[] fieldNames = GetFieldNames(items); // This returns ["Name", "Value"]
That works fine.
The problem
I need to get the Description (if it exists), so that GetFieldNames(items) returns ["Name", "The value"]
How do I modify the GetFieldNames() function to read the Description attribute if it exists?
(Please note that this function has been simplified, the real function is much more complex, so please avoid changing the logic)
This should work for you:
return properties.Select(p =>
Attribute.IsDefined(p, typeof(DescriptionAttribute)) ?
(Attribute.GetCustomAttribute(p, typeof(DescriptionAttribute)) as DescriptionAttribute).Description:
p.Name
).ToArray();
NOTE: just add using System.Reflection as GetCustomAttribute is an extension method in .Net 4.5
public static Tuple<string,string>[] GetFieldNames<T>(IEnumerable<T> items) where T : class
{
var result =
typeof (T).GetProperties()
.Where(p => SystemTypes.Contains(p.PropertyType) &&p.GetCustomAttribute<DescriptionAttribute>() != null)
.Select(
p =>
new Tuple<string, string>(p.Name,
p.GetCustomAttribute<DescriptionAttribute>().Description));
return result.ToArray();
}
for earlier version of .Net framework we can use this extension method:
public static class Extension
{
public static T GetCustomAttribute<T>(this System.Reflection.MemberInfo mi) where T : Attribute
{
return mi.GetCustomAttributes(typeof (T),true).FirstOrDefault() as T;
}
}
This is a generic function you can make use of, if the fieldName has description tag attribute it return the value otherwise it return null.
public string GetDescription<T>(string fieldName)
{
string result;
FieldInfo fi = typeof(T).GetField(fieldName.ToString());
if (fi != null)
{
try
{
object[] descriptionAttrs = fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
DescriptionAttribute description = (DescriptionAttribute)descriptionAttrs[0];
result = (description.Description);
}
catch
{
result = null;
}
}
else
{
result = null;
}
return result;
}
Example:
class MyClass {
public string Name { get; set; }
[Description("The age description")]
public int Age { get; set; }
}
string ageDescription = GetDescription<MyClass>(nameof(Age));
console.log(ageDescription) // OUTPUT: The age description
I am using Description attribute a lot so I wrote Nuget for this purpose.
With it you could just call:
typeof(TestClass).GetPropertyDescription("PropertyName");
It also allows to take DescriptionAttribute from class, field, enum and method.
use GetCustomAttributes Method
static void attributecheck()
{
var props = typeof(Product).GetProperties();
foreach (var propertyInfo in props)
{
var att = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
if (att.Length >0)
{
}
}
}
I have to run comparisons between thousands of pairs of objects, and then perform actions depending on the differences.
Is there an "accepted" way of doing this?
class ObjectA
{
public string FieldA { get; set; }
public string FieldB { get; set; }
public string FieldC { get; set; }
}
class ObjectB
{
public string FieldA { get; set; }
public string FieldB { get; set; }
public string FieldC { get; set; }
public bool Equals(ObjectA obj)
{
if ((object)obj == null) return false;
if (this.FieldA != obj.FieldA) return false;
if (this.FieldB != obj.FieldB) return false;
if (this.FieldC != obj.FieldC) return false;
return true;
}
}
void test()
{
ObjectA a = new ObjectA();
ObjectB b = new ObjectB();
if (b.Equals(a))
{
Console.WriteLine("Same!!");
}
}
That does a fairly simple test to determine if b=a, but I also want to know what is different between them.
Should I add a differences() method that returns a list of properties? That seems a bit not.net though, as then I'll be bandying about strings.
public List<string> Differences(ObjectA obj)
{
List<string> differences = new List<string>();
if ((object)obj == null)
{
differences.Add("null");
}
else
{
if (this.FieldA != obj.FieldA) differences.Add("FieldA");
if (this.FieldB != obj.FieldB) differences.Add("FieldB");
if (this.FieldC != obj.FieldC) differences.Add("FieldC");
}
return differences;
}
Also that seems much slower than the first, as I would be creating all those List<string>, and not short-cutting the comparisons. Or is that just the price I pay for having the extra information?
Maybe you should try this:
http://comparenetobjects.codeplex.com/
All credit to the author...
Edit: Since codeplex is shutting down, the github url : https://github.com/GregFinzer/Compare-Net-Objects
There is nothing built in that will allow you to represent partial objects (i.e the differences).
Your approach seems reasonable for what you are trying to achieve.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
class ObjectA
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public DateTime PropertyD { get; set; }
public string FieldA;
public DateTime FieldB;
}
class ObjectB
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public DateTime PropertyD { get; set; }
public string FieldA;
public DateTime FieldB;
}
class Program
{
static void Main(string[] args)
{
// create two objects with same properties
ObjectA a = new ObjectA() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
ObjectB b = new ObjectB() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
// add fields to those objects
a.FieldA = "hello";
b.FieldA = "Something differnt";
if (a.ComparePropertiesTo(b))
{
Console.WriteLine("objects have the same properties");
}
else
{
Console.WriteLine("objects have diferent properties!");
}
if (a.CompareFieldsTo(b))
{
Console.WriteLine("objects have the same Fields");
}
else
{
Console.WriteLine("objects have diferent Fields!");
}
Console.Read();
}
}
public static class Utilities
{
public static bool ComparePropertiesTo(this Object a, Object b)
{
System.Reflection.PropertyInfo[] properties = a.GetType().GetProperties(); // get all the properties of object a
foreach (var property in properties)
{
var propertyName = property.Name;
var aValue = a.GetType().GetProperty(propertyName).GetValue(a, null);
object bValue;
try // try to get the same property from object b. maybe that property does
// not exist!
{
bValue = b.GetType().GetProperty(propertyName).GetValue(b, null);
}
catch
{
return false;
}
if (aValue == null && bValue == null)
continue;
if (aValue == null && bValue != null)
return false;
if (aValue != null && bValue == null)
return false;
// if properties do not match return false
if (aValue.GetHashCode() != bValue.GetHashCode())
{
return false;
}
}
return true;
}
public static bool CompareFieldsTo(this Object a, Object b)
{
System.Reflection.FieldInfo[] fields = a.GetType().GetFields(); // get all the properties of object a
foreach (var field in fields)
{
var fieldName = field.Name;
var aValue = a.GetType().GetField(fieldName).GetValue(a);
object bValue;
try // try to get the same property from object b. maybe that property does
// not exist!
{
bValue = b.GetType().GetField(fieldName).GetValue(b);
}
catch
{
return false;
}
if (aValue == null && bValue == null)
continue;
if (aValue == null && bValue != null)
return false;
if (aValue != null && bValue == null)
return false;
// if properties do not match return false
if (aValue.GetHashCode() != bValue.GetHashCode())
{
return false;
}
}
return true;
}
}
As long as the names of the properties are equal, you can do this using the property map of each object and linq. I've done this in the past, but I don't have the code in front of me at the moment, sorry.
Here's an example I used for unit testing two instances of the same object type. I was testing to ensure that the properties that were serialized to file and populated in a new instance of the same object type were the same. Note that this is using System.Reflection and that you are comparing instances of the same type.
//Assume yourobjectA and yourobjectB have already been instantiated and populated.
//loop throught he properties and compare
//they should all be set the same as the previous instance
PropertyInfo[] propertiesA = yourobjectA.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
PropertyInfo[] propertiesB = yourobjectB.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
int count = oldProperties.Length;
for (int i = 0; i < count; i++)
{
if ((propertiesA [i].CanRead) && (propertiesB [i].CanRead))
{
if (propertiesA [i].PropertyType == typeof(String))
{
object oldStringValue = (string)propertiesA[i].GetValue(yourobjectA, null);
object newStringValue = (string)propertiesB[i].GetValue(yourobjectB., null);
if(oldStringValue != newStringValue )
{
//Do something
}
}
if (propertiesA [i].PropertyType == typeof(Boolean))
{
object oldBoolValue = (bool)propertiesA [i].GetValue(yourobjectA, null);
object newBoolValue = (bool)propertiesB [i].GetValue(yourobjectB., null);
if(oldBoolValue != newBoolValue)
{
//Do something
}
}
}
}