Related
I would like to know if it is possible to get attributes of the enum values and not of the enum itself? For example, suppose I have the following enum:
using System.ComponentModel; // for DescriptionAttribute
enum FunkyAttributesEnum
{
[Description("Name With Spaces1")]
NameWithoutSpaces1,
[Description("Name With Spaces2")]
NameWithoutSpaces2
}
What I want is given the enum type, produce 2-tuples of enum string value and its description.
Value was easy:
Array values = System.Enum.GetValues(typeof(FunkyAttributesEnum));
foreach (int value in values)
Tuple.Value = Enum.GetName(typeof(FunkyAttributesEnum), value);
But how do I get description attribute's value, to populate Tuple.Desc? I can think of how to do it if the Attribute belongs to the enum itself, but I am at a loss as to how to get it from the value of the enum.
This should do what you need.
try
{
var enumType = typeof(FunkyAttributesEnum);
var memberInfos =
enumType.GetMember(FunkyAttributesEnum.NameWithoutSpaces1.ToString());
var enumValueMemberInfo = memberInfos.FirstOrDefault(m =>
m.DeclaringType == enumType);
var valueAttributes =
enumValueMemberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
var description = ((DescriptionAttribute)valueAttributes[0]).Description;
}
catch
{
return FunkyAttributesEnum.NameWithoutSpaces1.ToString()
}
This piece of code should give you a nice little extension method on any enum that lets you retrieve a generic attribute. I believe it's different to the lambda function above because it's simpler to use and slightly - you only need to pass in the generic type.
public static class EnumHelper
{
/// <summary>
/// Gets an attribute on an enum field value
/// </summary>
/// <typeparam name="T">The type of the attribute you want to retrieve</typeparam>
/// <param name="enumVal">The enum value</param>
/// <returns>The attribute of type T that exists on the enum value</returns>
/// <example><![CDATA[string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description;]]></example>
public static T GetAttributeOfType<T>(this Enum enumVal) where T:System.Attribute
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
return (attributes.Length > 0) ? (T)attributes[0] : null;
}
}
Usage would then be:
string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description;
This is a generic implementation using a lambda for the selection
public static Expected GetAttributeValue<T, Expected>(this Enum enumeration, Func<T, Expected> expression)
where T : Attribute
{
T attribute =
enumeration
.GetType()
.GetMember(enumeration.ToString())
.Where(member => member.MemberType == MemberTypes.Field)
.FirstOrDefault()
.GetCustomAttributes(typeof(T), false)
.Cast<T>()
.SingleOrDefault();
if (attribute == null)
return default(Expected);
return expression(attribute);
}
Call it like this:
string description = targetLevel.GetAttributeValue<DescriptionAttribute, string>(x => x.Description);
I've merged a couple of the answers here to create a little more extensible solution. I'm providing it just in case it's helpful to anyone else in the future. Original posting here.
using System;
using System.ComponentModel;
public static class EnumExtensions {
// This extension method is broken out so you can use a similar pattern with
// other MetaData elements in the future. This is your base method for each.
public static T GetAttribute<T>(this Enum value) where T : Attribute {
var type = value.GetType();
var memberInfo = type.GetMember(value.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false);
return attributes.Length > 0
? (T)attributes[0]
: null;
}
// This method creates a specific call to the above method, requesting the
// Description MetaData attribute.
public static string ToName(this Enum value) {
var attribute = value.GetAttribute<DescriptionAttribute>();
return attribute == null ? value.ToString() : attribute.Description;
}
}
This solution creates a pair of extension methods on Enum. The first allows you to use reflection to retrieve any attribute associated with your value. The second specifically calls retrieves the DescriptionAttribute and returns it's Description value.
As an example, consider using the DescriptionAttribute attribute from System.ComponentModel
using System.ComponentModel;
public enum Days {
[Description("Sunday")]
Sun,
[Description("Monday")]
Mon,
[Description("Tuesday")]
Tue,
[Description("Wednesday")]
Wed,
[Description("Thursday")]
Thu,
[Description("Friday")]
Fri,
[Description("Saturday")]
Sat
}
To use the above extension method, you would now simply call the following:
Console.WriteLine(Days.Mon.ToName());
or
var day = Days.Mon;
Console.WriteLine(day.ToName());
In addition to AdamCrawford response, I've further created a more specialized extension methods that feed of it to get the description.
public static string GetAttributeDescription(this Enum enumValue)
{
var attribute = enumValue.GetAttributeOfType<DescriptionAttribute>();
return attribute == null ? String.Empty : attribute.Description;
}
hence, to get the description, you could either use the original extension method as
string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description
or you could simply call the the extension method here as:
string desc = myEnumVariable.GetAttributeDescription();
Which should hopefully make your code a bit more readable.
Fluent one liner...
Here I'm using the DisplayAttribute which contains both the Name and Description properties.
public static DisplayAttribute GetDisplayAttributesFrom(this Enum enumValue, Type enumType)
{
return enumType.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
}
Example
public enum ModesOfTransport
{
[Display(Name = "Driving", Description = "Driving a car")] Land,
[Display(Name = "Flying", Description = "Flying on a plane")] Air,
[Display(Name = "Sea cruise", Description = "Cruising on a dinghy")] Sea
}
void Main()
{
ModesOfTransport TransportMode = ModesOfTransport.Sea;
DisplayAttribute metadata = TransportMode.GetDisplayAttributesFrom(typeof(ModesOfTransport));
Console.WriteLine("Name: {0} \nDescription: {1}", metadata.Name, metadata.Description);
}
Output
Name: Sea cruise
Description: Cruising on a dinghy
Here is code to get information from a Display attribute. It uses a generic method to retrieve the attribute. If the attribute is not found it converts the enum value to a string with pascal/camel case converted to title case (code obtained here)
public static class EnumHelper
{
// Get the Name value of the Display attribute if the
// enum has one, otherwise use the value converted to title case.
public static string GetDisplayName<TEnum>(this TEnum value)
where TEnum : struct, IConvertible
{
var attr = value.GetAttributeOfType<TEnum, DisplayAttribute>();
return attr == null ? value.ToString().ToSpacedTitleCase() : attr.Name;
}
// Get the ShortName value of the Display attribute if the
// enum has one, otherwise use the value converted to title case.
public static string GetDisplayShortName<TEnum>(this TEnum value)
where TEnum : struct, IConvertible
{
var attr = value.GetAttributeOfType<TEnum, DisplayAttribute>();
return attr == null ? value.ToString().ToSpacedTitleCase() : attr.ShortName;
}
/// <summary>
/// Gets an attribute on an enum field value
/// </summary>
/// <typeparam name="TEnum">The enum type</typeparam>
/// <typeparam name="T">The type of the attribute you want to retrieve</typeparam>
/// <param name="value">The enum value</param>
/// <returns>The attribute of type T that exists on the enum value</returns>
private static T GetAttributeOfType<TEnum, T>(this TEnum value)
where TEnum : struct, IConvertible
where T : Attribute
{
return value.GetType()
.GetMember(value.ToString())
.First()
.GetCustomAttributes(false)
.OfType<T>()
.LastOrDefault();
}
}
And this is the extension method for strings for converting to title case:
/// <summary>
/// Converts camel case or pascal case to separate words with title case
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string ToSpacedTitleCase(this string s)
{
//https://stackoverflow.com/a/155486/150342
CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;
TextInfo textInfo = cultureInfo.TextInfo;
return textInfo
.ToTitleCase(Regex.Replace(s,
"([a-z](?=[A-Z0-9])|[A-Z](?=[A-Z][a-z]))", "$1 "));
}
Get the dictionary from enum.
public static IDictionary<string, int> ToDictionary(this Type enumType)
{
return Enum.GetValues(enumType)
.Cast<object>()
.ToDictionary(v => ((Enum)v).ToEnumDescription(), k => (int)k);
}
Now call this like...
var dic = typeof(ActivityType).ToDictionary();
EnumDecription Ext Method
public static string ToEnumDescription(this Enum en) //ext method
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((DescriptionAttribute)attrs[0]).Description;
}
return en.ToString();
}
public enum ActivityType
{
[Description("Drip Plan Email")]
DripPlanEmail = 1,
[Description("Modification")]
Modification = 2,
[Description("View")]
View = 3,
[Description("E-Alert Sent")]
EAlertSent = 4,
[Description("E-Alert View")]
EAlertView = 5
}
I this answer to setup a combo box from an enum attributes which was great.
I then needed to code the reverse so that I can get the selection from the box and return the enum in the correct type.
I also modified the code to handle the case where an attribute was missing
For the benefits of the next person, here is my final solution
public static class Program
{
static void Main(string[] args)
{
// display the description attribute from the enum
foreach (Colour type in (Colour[])Enum.GetValues(typeof(Colour)))
{
Console.WriteLine(EnumExtensions.ToName(type));
}
// Get the array from the description
string xStr = "Yellow";
Colour thisColour = EnumExtensions.FromName<Colour>(xStr);
Console.ReadLine();
}
public enum Colour
{
[Description("Colour Red")]
Red = 0,
[Description("Colour Green")]
Green = 1,
[Description("Colour Blue")]
Blue = 2,
Yellow = 3
}
}
public static class EnumExtensions
{
// This extension method is broken out so you can use a similar pattern with
// other MetaData elements in the future. This is your base method for each.
public static T GetAttribute<T>(this Enum value) where T : Attribute
{
var type = value.GetType();
var memberInfo = type.GetMember(value.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false);
// check if no attributes have been specified.
if (((Array)attributes).Length > 0)
{
return (T)attributes[0];
}
else
{
return null;
}
}
// This method creates a specific call to the above method, requesting the
// Description MetaData attribute.
public static string ToName(this Enum value)
{
var attribute = value.GetAttribute<DescriptionAttribute>();
return attribute == null ? value.ToString() : attribute.Description;
}
/// <summary>
/// Find the enum from the description attribute.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="desc"></param>
/// <returns></returns>
public static T FromName<T>(this string desc) where T : struct
{
string attr;
Boolean found = false;
T result = (T)Enum.GetValues(typeof(T)).GetValue(0);
foreach (object enumVal in Enum.GetValues(typeof(T)))
{
attr = ((Enum)enumVal).ToName();
if (attr == desc)
{
result = (T)enumVal;
found = true;
break;
}
}
if (!found)
{
throw new Exception();
}
return result;
}
}
}
Performance matters
If you want better performance this is the way to go:
public static class AdvancedEnumExtensions
{
/// <summary>
/// Gets the custom attribute <typeparamref name="T"/> for the enum constant, if such a constant is defined and has such an attribute; otherwise null.
/// </summary>
public static T GetCustomAttribute<T>(this Enum value) where T : Attribute
{
return GetField(value)?.GetCustomAttribute<T>(inherit: false);
}
/// <summary>
/// Gets the FieldInfo for the enum constant, if such a constant is defined; otherwise null.
/// </summary>
public static FieldInfo GetField(this Enum value)
{
ulong u64 = ToUInt64(value);
return value
.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
.Where(f => ToUInt64(f.GetRawConstantValue()) == u64)
.FirstOrDefault();
}
/// <summary>
/// Checks if an enum constant is defined for this enum value
/// </summary>
public static bool IsDefined(this Enum value)
{
return GetField(value) != null;
}
/// <summary>
/// Converts the enum value to UInt64
/// </summary>
public static ulong ToUInt64(this Enum value) => ToUInt64((object)value);
private static ulong ToUInt64(object value)
{
switch (Convert.GetTypeCode(value))
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
return unchecked((ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture));
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Boolean:
return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
default: throw new InvalidOperationException("UnknownEnumType");
}
}
}
Why does this have better performance?
Because the built-in methods all use code very similar to this except they also run a bunch of other code we don't care about. C#'s Enum code is quite horrible in general.
The above code has been Linq-ified and streamlined so it only contains the bits we care about.
Why is the built-in code slow?
First regarding Enum.ToString() -vs- Enum.GetName(..)
Always use the latter. (Or better yet neither, as will become clear below.)
ToString() uses the latter internally, but again, also does a bunch of other stuff we don't want, e.g. tries to combine flags, print out numbers etc. We are only interested in constants defined inside the enum.
Enum.GetName in turn gets all fields, creates a string array for all names, uses the above ToUInt64 on all of their RawConstantValues to create an UInt64 array of all values, sorts both arrays according to the UInt64 value, and finally gets the name from the name-array by doing a BinarySearch in the UInt64-array to find the index of the value we wanted.
...and then we throw the fields and the sorted arrays away use that name to find the field again.
One word: "Ugh!"
For some programmer humor, a one liner as a joke:
public static string GetDescription(this Enum value) => value.GetType().GetMember(value.ToString()).First().GetCustomAttribute<DescriptionAttribute>() is DescriptionAttribute attribute ? attribute.Description : string.Empty;
In a more readable form:
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
public static class EnumExtensions
{
// get description from enum:
public static string GetDescription(this Enum value)
{
return value.GetType().
GetMember(value.ToString()).
First().
GetCustomAttribute<DescriptionAttribute>() is DescriptionAttribute attribute
? attribute.Description
: throw new Exception($"Enum member '{value.GetType()}.{value}' doesn't have a [DescriptionAttribute]!");
}
// get enum from description:
public static T GetEnum<T>(this string description) where T : Enum
{
foreach (FieldInfo fieldInfo in typeof(T).GetFields())
{
if (fieldInfo.GetCustomAttribute<DescriptionAttribute>() is DescriptionAttribute attribute && attribute.Description == description)
return (T)fieldInfo.GetRawConstantValue();
}
throw new Exception($"Enum '{typeof(T)}' doesn't have a member with a [DescriptionAttribute('{description}')]!");
}
}
Here's the .NET Core version of AdamCrawford's answer, using System.Reflection.TypeExtensions;
public static class EnumHelper
{
/// <summary>
/// Gets an attribute on an enum field value
/// </summary>
/// <typeparam name="T">The type of the attribute you want to retrieve</typeparam>
/// <param name="enumVal">The enum value</param>
/// <returns>The attribute of type T that exists on the enum value</returns>
/// <example>string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description;</example>
public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
IEnumerable<Attribute> attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
return (T)attributes?.ToArray()[0];
}
}
Adding my solution for Net Framework and NetCore.
I used this for my Net Framework implementation:
public static class EnumerationExtension
{
public static string Description( this Enum value )
{
// get attributes
var field = value.GetType().GetField( value.ToString() );
var attributes = field.GetCustomAttributes( typeof( DescriptionAttribute ), false );
// return description
return attributes.Any() ? ( (DescriptionAttribute)attributes.ElementAt( 0 ) ).Description : "Description Not Found";
}
}
This doesn't work for NetCore so I modified it to do this:
public static class EnumerationExtension
{
public static string Description( this Enum value )
{
// get attributes
var field = value.GetType().GetField( value.ToString() );
var attributes = field.GetCustomAttributes( false );
// Description is in a hidden Attribute class called DisplayAttribute
// Not to be confused with DisplayNameAttribute
dynamic displayAttribute = null;
if (attributes.Any())
{
displayAttribute = attributes.ElementAt( 0 );
}
// return description
return displayAttribute?.Description ?? "Description Not Found";
}
}
Enumeration Example:
public enum ExportTypes
{
[Display( Name = "csv", Description = "text/csv" )]
CSV = 0
}
Sample Usage for either static added:
var myDescription = myEnum.Description();
If your enum contains a value like Equals you might bump into a few bugs using some extensions in a lot of answers here. This is because it is normally assumed that typeof(YourEnum).GetMember(YourEnum.Value) would return only one value, which is the MemberInfo of your enum. Here's a slightly safer version Adam Crawford's answer.
public static class AttributeExtensions
{
#region Methods
public static T GetAttribute<T>(this Enum enumValue) where T : Attribute
{
var type = enumValue.GetType();
var memberInfo = type.GetMember(enumValue.ToString());
var member = memberInfo.FirstOrDefault(m => m.DeclaringType == type);
var attribute = Attribute.GetCustomAttribute(member, typeof(T), false);
return attribute is T ? (T)attribute : null;
}
#endregion
}
Model
The model in which we fill our values
public class MemberTypeModel : IDto
{
public string MemberAttributeName { get; set; }
public string MemberName { get; set; }
public int MemberValue { get; set; }
}
Enum
Our target is the enum
public enum MemberType
{
[FieldText("Yönetim Kurul Üyesi")]
BoardManager = 0,
[FieldText("Temsilci")]
Representative = 1,
[FieldText("Üye")]
Member = 2
}
Helper Method
The helper method we will use to fetch the custom attribute object
public T GetMemberCustomText<T>(MemberType memberType) where T : Attribute
{
var enumType = memberType.GetType();
var name = Enum.GetName(enumType, memberType);
return enumType.GetField(name).GetCustomAttributes(false).OfType<T>().SingleOrDefault();
}
Get Method
First we pull enum values and cast them to enum type. Then, with the Linq selection query we know;
MemberAttributeName field with helper method,
MemberName field with Enum.GetName method,
Casting the MemberValue field to an int type as well,
We fill it out and turn it into a list.
public List<MemberTypeModel> GetMemberTypes()
{
var memberTypes = Enum.GetValues(typeof(MemberType))
.Cast<MemberType>()
.Select(et => new MemberTypeModel
{
MemberAttributeName = GetMemberCustomText<FieldText>(et).Text,
MemberName = Enum.GetName(et.GetType(), et),
MemberValue = (int)et
}).ToList();
return memberTypes;
}
I implemented this extension method to get the description from enum values. It works for all kind of enums.
public static class EnumExtension
{
public static string ToDescription(this System.Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}
}
Taking advantage of some of the newer C# language features, you can reduce the line count:
public static TAttribute GetEnumAttribute<TAttribute>(this Enum enumVal) where TAttribute : Attribute
{
var memberInfo = enumVal.GetType().GetMember(enumVal.ToString());
return memberInfo[0].GetCustomAttributes(typeof(TAttribute), false).OfType<TAttribute>().FirstOrDefault();
}
public static string GetEnumDescription(this Enum enumValue) => enumValue.GetEnumAttribute<DescriptionAttribute>()?.Description ?? enumValue.ToString();
Bryan Rowe and AdamCrawford thanks for your answers!
But if somebody need method for get Discription (not extension) you can use it:
string GetEnumDiscription(Enum EnumValue)
{
var type = EnumValue.GetType();
var memInfo = type.GetMember(EnumValue.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attributes.Length > 0) ? ((DescriptionAttribute)attributes[0]).Description : null;
}
This extension method will obtain a string representation of an enum value using its XmlEnumAttribute. If no XmlEnumAttribute is present, it falls back to enum.ToString().
public static string ToStringUsingXmlEnumAttribute<T>(this T enumValue)
where T: struct, IConvertible
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("T must be an enumerated type");
}
string name;
var type = typeof(T);
var memInfo = type.GetMember(enumValue.ToString());
if (memInfo.Length == 1)
{
var attributes = memInfo[0].GetCustomAttributes(typeof(System.Xml.Serialization.XmlEnumAttribute), false);
if (attributes.Length == 1)
{
name = ((System.Xml.Serialization.XmlEnumAttribute)attributes[0]).Name;
}
else
{
name = enumValue.ToString();
}
}
else
{
name = enumValue.ToString();
}
return name;
}
And if you want the full list of names you can do something like
typeof (PharmacyConfigurationKeys).GetFields()
.Where(x => x.GetCustomAttributes(false).Any(y => typeof(DescriptionAttribute) == y.GetType()))
.Select(x => ((DescriptionAttribute)x.GetCustomAttributes(false)[0]).Description);
public enum DataFilters
{
[Display(Name= "Equals")]
Equals = 1,// Display Name and Enum Name are same
[Display(Name= "Does Not Equal")]
DoesNotEqual = 2, // Display Name and Enum Name are different
}
Now it will produce error in this case 1 "Equals"
public static string GetDisplayName(this Enum enumValue)
{
var enumMember = enumValue.GetType().GetMember(enumValue.ToString()).First();
return enumMember.GetCustomAttribute<DisplayAttribute>() != null ? enumMember.GetCustomAttribute<DisplayAttribute>().Name : enumMember.Name;
}
so if it is same return enum name rather than display name because
enumMember.GetCustomAttribute()
gets null if displayname and enum name are same.....
The NuGet package Enums.Net has good support for this:
var value = FunkyAttributesEnum.NameWithoutSpaces1;
string description = value.AsString(EnumFormat.Description); // => "Name With Spaces1"
The package is simple, intuitive, and complete.
It's type-safe and has cache to avoid recurring reflection.
The GitHub repository has more information, including the limitations of the native Enum and a demo of functionality:
Getting attributes;
Flag operations;
Enum formats (for ToString or to parse the enum from string);
Rich iteration of all values with Enums.GetMembers<MyEnum>();
Etc.
Alternatively, you could do the following:
Dictionary<FunkyAttributesEnum, string> description = new Dictionary<FunkyAttributesEnum, string>()
{
{ FunkyAttributesEnum.NameWithoutSpaces1, "Name With Spaces1" },
{ FunkyAttributesEnum.NameWithoutSpaces2, "Name With Spaces2" },
};
And get the description with the following:
string s = description[FunkyAttributesEnum.NameWithoutSpaces1];
In my opinion this is a more efficient way of doing what you want to accomplish, as no reflection is needed..
Guys if it helps I will share with you my solution:
Definition of Custom attribute:
[AttributeUsage(AttributeTargets.Field,AllowMultiple = false)]
public class EnumDisplayName : Attribute
{
public string Name { get; private set; }
public EnumDisplayName(string name)
{
Name = name;
}
}
Now because I needed it inside of HtmlHelper definition of HtmlHelper Extension:
public static class EnumHelper
{
public static string EnumDisplayName(this HtmlHelper helper,EPriceType priceType)
{
//Get every fields from enum
var fields = priceType.GetType().GetFields();
//Foreach field skipping 1`st fieldw which keeps currently sellected value
for (int i = 0; i < fields.Length;i++ )
{
//find field with same int value
if ((int)fields[i].GetValue(priceType) == (int)priceType)
{
//get attributes of found field
var attributes = fields[i].GetCustomAttributes(false);
if (attributes.Length > 0)
{
//return name of found attribute
var retAttr = (EnumDisplayName)attributes[0];
return retAttr.Name;
}
}
}
//throw Error if not found
throw new Exception("Błąd podczas ustalania atrybutów dla typu ceny allegro");
}
}
Hope it helps
Alternatively, you could do the following:
List<SelectListItem> selectListItems = new List<SelectListItem>();
foreach (var item in typeof(PaymentTerm).GetEnumValues())
{
var type = item.GetType();
var name = type.GetField(item.ToString()).GetCustomAttributesData().FirstOrDefault()?.NamedArguments.FirstOrDefault().TypedValue.Value.ToString();
selectListItems.Add(new SelectListItem(name, type.Name));
}
This is how I solved it without using custom helpers or extensions with .NET core 3.1.
Class
public enum YourEnum
{
[Display(Name = "Suryoye means Arameans")]
SURYOYE = 0,
[Display(Name = "Oromoye means Syriacs")]
OROMOYE = 1,
}
Razor
#using Enumerations
foreach (var name in Html.GetEnumSelectList(typeof(YourEnum)))
{
<h1>#name.Text</h1>
}
I have created an extension method that will return description of all the elements in an enum in C#.
public static List<string> GetAllEnumDescriptions(this Type enumType)
{
try
{
var enumList = Enum.GetValues(enumType).Cast<Enum>().ToList();
List<string> result = new List<string>();
foreach (var enumItem in enumList)
{
result.Add(enumItem.EnumDescription());
}
return result;
}
catch (Exception ex)
{
return new List<string>();
}
}
This method will add the description of the elements in an enum using the inbuilt EnumDescription() extension method.
I want to copy the values of Properties in a given object ClassA to another object instance called ClassB, these classes may or may not be the same type.
if a property in ClassB has a value and in ClassA the corresponding property value is null, then do not copy that value, so only copy across where the current property in ClassB is null.
This is NOT a clone exercise, the target object (ClassB) is already instantiated with partially defined values, I'm looking for a reusable way to copy across the rest of the values that were not already set.
Think of testing scenarios where we have a common or default test data value, for specific tests I want to set some specific fields, then finish setting the other properties from the common test data object.
I think I am looking for a Reflection based solution, as that way we would not need to know the specific types to copy, which would make it reusable for many different scenarios.
eg.
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public Address ContactAddress { get; set; }
}
public class Address
{
public string Address1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
test eg.
public void TestMethod1()
{
Employee employee = new Employee();
employee.EmployeeID = 100;
employee.EmployeeName = "John";
employee.ContactAddress = new Address();
employee.ContactAddress.Address1 = "Park Ave";
employee.ContactAddress.City = "New York";
employee.ContactAddress.State = "NewYork";
employee.ContactAddress.ZipCode = "10002";
Employee employeeCopy = new Employee();
employeeCopy.EmployeeID = 101;
employeeCopy.EmployeeName = "Tom";
employeeCopy.ContactAddress = new Address();
CopyPropertiesTo(employee, employeeCopy);
}
I want to get the result
employeeCopy EmployeeID=101;
EmployeeName="Tom";
ContactAddress.Address1 = "Park Ave";
ContactAddress.City = "New York";
ContactAddress.State = "NewYork";
ContactAddress.ZipCode = "10002"
So in this case, because none of the fields in employeeCopy.ContactAddress have been set, only those fields from the original employee object should be copied across.
I can not figure out how to write the method:
CopyPropertiesTo(object sourceObject, object targetObject)
One way to do this is to simply check each property in the "to" Employee, and if it's null or 0, assign it the value from the "from" Employee:
/// <summary>
/// Copies values in 'from' to 'to' if they are null in 'to'
/// </summary>
public static void CopyProperties(Employee from, Employee to)
{
if (from == null) return;
if (to == null) to = new Employee();
if (to.EmployeeID == 0) to.EmployeeID = from.EmployeeID;
if (to.EmployeeName == null) to.EmployeeName = from.EmployeeName;
if (from.ContactAddress == null) return;
if (to.ContactAddress == null) to.ContactAddress = new Address();
if (to.ContactAddress.Address1 == null)
to.ContactAddress.Address1 = from.ContactAddress.Address1;
if (to.ContactAddress.City == null)
to.ContactAddress.City = from.ContactAddress.City;
if (to.ContactAddress.State == null)
to.ContactAddress.State = from.ContactAddress.State;
if (to.ContactAddress.ZipCode == null)
to.ContactAddress.ZipCode = from.ContactAddress.ZipCode;
}
Here is my suggestions too if not too late, but mayby helps.
public class Source
{
[DefaultValueAttribute(-1)]
public int Property { get; set; }
public int AnotherProperty { get; set; }
}
public class Dedstination
{
public int Property { get; set; }
[DefaultValueAttribute(42)]
public int AnotherProperty { get; set; }
}
public void Main()
{
var source = new Source { Property = 10, AnotherProperty = 76 };
var destination = new Dedstination();
MapValues(source, destination);
}
public static void MapValues<TS, TD>(TS source, TD destination)
{
var srcPropsWithValues = typeof(TS)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(x => x.Name, y => y.GetValue(source));
var dstProps = typeof(TD)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(key => key, value => value.GetCustomAttribute<DefaultValueAttribute>()?.Value
?? (value.PropertyType.IsValueType
? Activator.CreateInstance(value.PropertyType, null)
: null));
foreach (var prop in dstProps)
{
var destProperty = prop.Key;
if (srcPropsWithValues.ContainsKey(destProperty.Name))
{
var defaultValue = prop.Value;
var currentValue = destProperty.GetValue(destination);
var sourceValue = srcPropsWithValues[destProperty.Name];
if (currentValue.Equals(defaultValue) && !sourceValue.Equals(defaultValue))
{
destProperty.SetValue(destination, sourceValue);
}
}
}
}
EDIT: I edited my solution in order to remove the dependency on using DefaultValueAttribute. Now you can take a default value either from the attributes if specified or the type defaults.
Previous solution was as follows:
// This solution do not needs DefaultValueAttributes
var dstProps = typeof(TD)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(x => x, x => x.PropertyType.IsValueType ? Activator.CreateInstance(x.PropertyType, null) : null);
// This solution needs DefaultValueAttributes
var dstProps = typeof(TD)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(x => x, x => x.GetCustomAttribute<DefaultValueAttribute>()?.Value ?? null);
Deep cloning can be achieved easily through serialization, however to only copy across non-null fields needs more conditional logic, In this case I call this a Coalesce so I've named my Method CoalesceTo. You could refactor this into an extension method if you wanted to, but I wouldn't recommend it, instead put this inside a static helper class. As useful as this might be, I don't encourage it as your "goto" for a production business runtime.
Using Reflection for these types of solutions is usually the most inefficient mechanism, but it gives us a lot of flexibility and is great for mocking, prototyping and quick unit test expressions.
Although not in this example, it would be easy to add in checks to exclude [Obsolete] properties for advanced scenarios
The following example uses Property Name comparison, so you don't have to pass in objects of the same type. Notice that IsNull and IsValueType methods have been created to encapsulate those concepts, simplifying tweaks you might want to make to this method.
This method also checks that properties can be read/written before proceeding, which allows us to support readonly properties on the source object, and of course we don't try to write to readonly properties.
The final value parse and write is wrapped in a try catch statement that is suppressing any errors, It takes a bit of tweaking to get code like this to work universally, but it should work fine for simple type definitions.
/// <summary>
/// Deep Copy the top level properties from this object only if the corresponding property on the target object IS NULL.
/// </summary>
/// <param name="source">the source object to copy from</param>
/// <param name="target">the target object to update</param>
/// <returns>A reference to the Target instance for chaining, no changes to this instance.</returns>
public static void CoalesceTo(object source, object target, StringComparison propertyComparison = StringComparison.OrdinalIgnoreCase)
{
var sourceType = source.GetType();
var targetType = target.GetType();
var targetProperties = targetType.GetProperties();
foreach(var sourceProp in sourceType.GetProperties())
{
if(sourceProp.CanRead)
{
var sourceValue = sourceProp.GetValue(source);
// Don't copy across nulls or defaults
if (!IsNull(sourceValue, sourceProp.PropertyType))
{
var targetProp = targetProperties.FirstOrDefault(x => x.Name.Equals(sourceProp.Name, propertyComparison));
if (targetProp != null && targetProp.CanWrite)
{
if (!targetProp.CanRead)
continue; // special case, if we cannot verify the destination, assume it has a value.
else if (targetProp.PropertyType.IsArray || targetProp.PropertyType.IsGenericType // It is ICollection<T> or IEnumerable<T>
&& targetProp.PropertyType.GenericTypeArguments.Any()
&& targetProp.PropertyType.GetGenericTypeDefinition() != typeof(Nullable<>) // because that will also resolve GetElementType!
)
continue; // special case, skip arrays and collections...
else
{
// You can do better than this, for now if conversion fails, just skip it
try
{
var existingValue = targetProp.GetValue(target);
if (IsValueType(targetProp.PropertyType))
{
// check that the destination is NOT already set.
if (IsNull(existingValue, targetProp.PropertyType))
{
// we do not overwrite a non-null destination value
object targetValue = sourceValue;
if (!targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
{
// TODO: handle specific types that don't go across.... or try some brute force type conversions if neccessary
if (targetProp.PropertyType == typeof(string))
targetValue = targetValue.ToString();
else
targetValue = Convert.ChangeType(targetValue, targetProp.PropertyType);
}
targetProp.SetValue(target, targetValue);
}
}
else if (!IsValueType(sourceProp.PropertyType))
{
// deep clone
if (existingValue == null)
existingValue = Activator.CreateInstance(targetProp.PropertyType);
CoalesceTo(sourceValue, existingValue);
}
}
catch (Exception)
{
// suppress exceptions, don't set a field that we can't set
}
}
}
}
}
}
}
/// <summary>
/// Check if a boxed value is null or not
/// </summary>
/// <remarks>
/// Evaluate your own logic or definition of null in here.
/// </remarks>
/// <param name="value">Value to inspect</param>
/// <param name="valueType">Type of the value, pass it in if you have it, otherwise it will be resolved through reflection</param>
/// <returns>True if the value is null or primitive default, otherwise False</returns>
public static bool IsNull(object value, Type valueType = null)
{
if (value is null)
return true;
if (valueType == null) valueType = value.GetType();
if (valueType.IsPrimitive || valueType.IsEnum || valueType.IsValueType)
{
// Handle nullable types like float? or Nullable<Int>
if (valueType.IsGenericType)
return value is null;
else
return Activator.CreateInstance(valueType).Equals(value);
}
// treat empty string as null!
if (value is string s)
return String.IsNullOrWhiteSpace(s);
return false;
}
/// <summary>
/// Check if a type should be copied by value or if it is a complexe type that should be deep cloned
/// </summary>
/// <remarks>
/// Evaluate your own logic or definition of Object vs Value/Primitive here.
/// </remarks>
/// <param name="valueType">Type of the value to check</param>
/// <returns>True if values of this type can be straight copied, false if they should be deep cloned</returns>
public static bool IsValueType(Type valueType)
{
// TODO: any specific business types that you want to treat as value types?
// Standard .Net Types that can be treated as value types
if (valueType.IsPrimitive || valueType.IsEnum || valueType.IsValueType || valueType == typeof(string))
return true;
// Support Nullable Types as Value types (Type.IsValueType) should deal with this, but just in case
if (valueType.HasElementType // It is array/enumerable/nullable
&& valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Nullable<>))
return true;
return false;
}
Because we are using reflection here, we cant take advantage of optimisations that Generics could offer us. If you wanted to adapt this to a production environment, consider using T4 templates to script out a Generic typed version of this logic as extension methods to your business types.
Deep Cloning -
You'll notice I specifically skip arrays and other IEnumerable structures... There's a whole can of worms in supporting them, it might be better to not let the one method attempt a Deep copy, so take the nested call to CoalesceTo out, then call the clone method on each object in the tree.
The problem with arrays/collections/lists is that before you could clone, you would need to identify a way to synchronise the collection in the source with the collection in the target, you could make a convention based on an Id field or some kind of attribute like [KeyAttribute] but that sort of implementation needs to be highly specific to your business logic and is outside of the scope of this already monstrous post ;)
Types like Decimal and DateTime are problematic in these types of scenarios, they should not be compared to null, instead we have to compare them to their default type states, again we can't use the generic default operator or value in this case because the type can only be resolved at runtime.
So I've changed your classes to include an example of how DateTimeOffset is handled by this logic:
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public DateTimeOffset Date { get; set; }
public float? Capacity { get; set; }
Nullable<int> MaxShift { get; set; }
public Address ContactAddress { get; set; }
}
public class Address
{
public string Address1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
public static void TestMethod1()
{
Employee employee = new Employee();
employee.EmployeeID = 100;
employee.EmployeeName = "John";
employee.Capacity = 26.2f;
employee.MaxShift = 8;
employee.Date = new DateTime(2020,1,22);
employee.ContactAddress = new Address();
employee.ContactAddress.Address1 = "Park Ave";
employee.ContactAddress.City = "New York";
employee.ContactAddress.State = "NewYork";
employee.ContactAddress.ZipCode = "10002";
Employee employeeCopy = new Employee();
employeeCopy.EmployeeID = 101;
employeeCopy.EmployeeName = "Tom";
employeeCopy.ContactAddress = new Address();
CoalesceTo(employee, employeeCopy);
}
This results in the following object graph:
{
"EmployeeID": 101,
"EmployeeName": "Tom",
"Date": "2020-01-22T00:00:00+11:00",
"Capacity":26.2,
"MaxShift":8,
"ContactAddress": {
"Address1": "Park Ave",
"City": "New York",
"State": "NewYork",
"ZipCode": "10002"
}
}
Make your changes to the new instance after the copy completes and/or implement ICloneable interface.
https://learn.microsoft.com/en-us/dotnet/api/system.icloneable?view=netcore-3.1
private Employee Check(Employee employee,Employee employeeCopy)
{
if (employeeCopy.EmployeeID==0 && employee.EmployeeID !=0)
{
employeeCopy.EmployeeID = employee.EmployeeID;
}
if (employeeCopy.EmployeeName == null && employee.EmployeeName != null)
{
employeeCopy.EmployeeName = employee.EmployeeName;
}
if (employeeCopy.ContactAddress == null)
{
if (employeeCopy.ContactAddress.Address1 == null && employee.ContactAddress.Address1 != null)
{
employeeCopy.ContactAddress.Address1 = employee.ContactAddress.Address1;
}
if (employeeCopy.ContactAddress.City == null && employee.ContactAddress.City != null)
{
employeeCopy.ContactAddress.City = employee.ContactAddress.City;
}
if (employeeCopy.ContactAddress.State == null && employee.ContactAddress.State != null)
{
employeeCopy.ContactAddress.State = employee.ContactAddress.State;
}
if (employeeCopy.ContactAddress.ZipCode == null && employee.ContactAddress.ZipCode != null)
{
employeeCopy.ContactAddress.ZipCode = employee.ContactAddress.ZipCode;
}
}
return employeeCopy;
}
Is this what you are looking for ?
public static void CopyPropertiesTo(Employee EP1, Employee EP2){
Type eType=typeof(Employee);
PropertyInfo[] eProps = eType.GetProperties();
foreach(var p in eProps){
if(p.PropertyType != typeof(String) && p.PropertyType != typeof(Int32)){
//Merging Contact Address
Type cType=p.PropertyType;
PropertyInfo[] cProps = cType.GetProperties();
foreach(var c in cProps){
//Check if value is null
if (String.IsNullOrEmpty((EP2.ContactAddress.GetType().GetProperty(c.Name).GetValue(EP2.ContactAddress) as string))){
//Assign Source to Target
EP2.ContactAddress.GetType().GetProperty(c.Name).SetValue(EP2.ContactAddress, (EP1.ContactAddress.GetType().GetProperty(c.Name).GetValue(EP1.ContactAddress)));
}
}
}
else{
//Check if value is null or empty
if (String.IsNullOrEmpty((EP2.GetType().GetProperty(p.Name).GetValue(EP2) as string))){
//Assign Source to Target
EP2.GetType().GetProperty(p.Name).SetValue(EP2, (EP1.GetType().GetProperty(p.Name).GetValue(EP1)));
}
}
}
}
Not the prettiest, but this should do it and allow you to change the names/amount of properties in the class. I haven't ever actually tried doing it like this so if someone out there has some feedback, I would appreciate it
Check out the following links for more info and examples
PropertyInfo
GetType
GetProperty
Instead of trying to do a deep copy at all, these types of issues are generally easier and less resource intensive if you do a full deep clone first, and then set your values.
There are many posts on SO regarding deep clone, my preference is just to use JSON.Net to serialize and then deserialize.
public static T Clone<T>(T value, Newtonsoft.Json.JsonSerializerSettings settings = null)
{
var objectType = value.GetType();
var cereal = Newtonsoft.Json.JsonConvert.SerializeObject(value, settings);
return (T)Newtonsoft.Json.JsonConvert.DeserializeObject(cereal, objectType, settings);
}
However this code requires the Newtonsoft.Json nuget package reference.
Cloning the object sets all the common/default values first, and then we only modify those properties that we need for this specific test, or code block.
public void TestMethod1()
{
Employee employee = new Employee();
employee.EmployeeID = 100;
employee.EmployeeName = "John";
employee.ContactAddress = new Address();
employee.ContactAddress.Address1 = "Park Ave";
employee.ContactAddress.City = "New York";
employee.ContactAddress.State = "NewYork";
employee.ContactAddress.ZipCode = "10002";
// Create a deep clone of employee
Employee employeeCopy = Clone(employee);
// set the specific fields that we want to change
employeeCopy.EmployeeID = 101;
employeeCopy.EmployeeName = "Tom";
}
Often we can find simpler solutions if we are open to changing our approach, this solution will have the same output as if we had conditionally copied across the property values, but without comparing anything.
If you have other reasons for a conditional copy, referred to in other solutions to this post as a Merge or Coalesce, then my other answer using reflection will do the job, but its not as robust as this one.
[TestClass]
public class UnitTest11
{
[TestMethod]
public void TestMethod1()
{
Employee employee = new Employee();
employee.EmployeeID = 100;
employee.EmployeeName = "John";
employee.Date = DateTime.Now;
employee.ContactAddress = new Address();
employee.ContactAddress.Address1 = "Park Ave";
employee.ContactAddress.City = "New York";
employee.ContactAddress.State = "NewYork";
employee.ContactAddress.ZipCode = "10002";
Employee employeeCopy = new Employee();
employeeCopy.EmployeeID = 101;
employeeCopy.EmployeeName = "Tom";
employeeCopy.ContactAddress = new Address();
employeeCopy.ContactAddress.City = "Bei Jing";
//copy all properties from employee to employeeCopy
CoalesceTo(employee, employeeCopy);
Console.ReadLine();
}
/// Deep Copy the top level properties from this object only if the corresponding property on the target object IS NULL.
/// </summary>
/// <param name="source">the source object to copy from</param>
/// <param name="target">the target object to update</param>
/// <returns>A reference to the Target instance for chaining, no changes to this instance.</returns>
public static void CoalesceTo(object source, object target, StringComparison propertyComparison = StringComparison.OrdinalIgnoreCase)
{
var sourceType = source.GetType();
var targetType = target.GetType();
var targetProperties = targetType.GetProperties();
foreach (var sourceProp in sourceType.GetProperties())
{
if (sourceProp.CanRead)
{
var sourceValue = sourceProp.GetValue(source);
// Don't copy across nulls or defaults
if (!IsNull(sourceValue, sourceProp.PropertyType))
{
var targetProp = targetProperties.FirstOrDefault(x => x.Name.Equals(sourceProp.Name, propertyComparison));
if (targetProp != null && targetProp.CanWrite)
{
if (!targetProp.CanRead)
continue; // special case, if we cannot verify the destination, assume it has a value.
else if (targetProp.PropertyType.IsArray || targetProp.PropertyType.IsGenericType // It is ICollection<T> or IEnumerable<T>
&& targetProp.PropertyType.GenericTypeArguments.Any()
&& targetProp.PropertyType.GetGenericTypeDefinition() != typeof(Nullable<>) // because that will also resolve GetElementType!
)
continue; // special case, skip arrays and collections...
else
{
// You can do better than this, for now if conversion fails, just skip it
try
{
var existingValue = targetProp.GetValue(target);
if (IsValueType(targetProp.PropertyType))
{
// check that the destination is NOT already set.
if (IsNull(existingValue, targetProp.PropertyType))
{
// we do not overwrite a non-null destination value
object targetValue = sourceValue;
if (!targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
{
// TODO: handle specific types that don't go across.... or try some brute force type conversions if neccessary
if (targetProp.PropertyType == typeof(string))
targetValue = targetValue.ToString();
else
targetValue = Convert.ChangeType(targetValue, targetProp.PropertyType);
}
targetProp.SetValue(target, targetValue);
}
}
else if (!IsValueType(sourceProp.PropertyType))
{
// deep clone
if (existingValue == null)
existingValue = Activator.CreateInstance(targetProp.PropertyType);
CoalesceTo(sourceValue, existingValue);
}
}
catch (Exception)
{
// suppress exceptions, don't set a field that we can't set
}
}
}
}
}
}
}
/// <summary>
/// Check if a boxed value is null or not
/// </summary>
/// <remarks>
/// Evaluate your own logic or definition of null in here.
/// </remarks>
/// <param name="value">Value to inspect</param>
/// <param name="valueType">Type of the value, pass it in if you have it, otherwise it will be resolved through reflection</param>
/// <returns>True if the value is null or primitive default, otherwise False</returns>
public static bool IsNull(object value, Type valueType = null)
{
if (value is null)
return true;
if (valueType == null) valueType = value.GetType();
if (valueType.IsPrimitive || valueType.IsEnum || valueType.IsValueType)
return value.Equals(Activator.CreateInstance(valueType));
// treat empty string as null!
if (value is string s)
return String.IsNullOrWhiteSpace(s);
return false;
}
/// <summary>
/// Check if a type should be copied by value or if it is a complexe type that should be deep cloned
/// </summary>
/// <remarks>
/// Evaluate your own logic or definition of Object vs Value/Primitive here.
/// </remarks>
/// <param name="valueType">Type of the value to check</param>
/// <returns>True if values of this type can be straight copied, false if they should be deep cloned</returns>
public static bool IsValueType(Type valueType)
{
// TODO: any specific business types that you want to treat as value types?
// Standard .Net Types that can be treated as value types
if (valueType.IsPrimitive || valueType.IsEnum || valueType.IsValueType || valueType == typeof(string))
return true;
// Support Nullable Types as Value types (Type.IsValueType) should deal with this, but just in case
if (valueType.HasElementType // It is array/enumerable/nullable
&& valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Nullable<>))
return true;
return false;
}
}
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public DateTimeOffset Date { get; set; }
public float? check { get; set; }
public Address ContactAddress { get; set; }
}
public class Address
{
public string Address1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
Thank you very much for every one,especially for #Chris Schaller I post the code above
I'm posting this question to find a simpler way of achieving a result.
We have a big IF statement that checks for NULL or string.empty. Something like this:
if (string.IsNullOrEmpty(Empl.Name) || string.IsNullOrEmpty(Empl.last) ||
string.IsNullOrEmpty(Empl.init) || string.IsNullOrEmpty(Empl.cat1) ||
string.IsNullOrEmpty(Empl.history) || string.IsNullOrEmpty(Empl.cat2) ||
string.IsNullOrEmpty(Empl.year) || string.IsNullOrEmpty(Empl.month) ||
string.IsNullOrEmpty(Empl.retire) || string.IsNullOrEmpty(Empl.spouse) ||
string.IsNullOrEmpty(Empl.children) || string.IsNullOrEmpty(Empl.bday) ||
string.IsNullOrEmpty(Empl.hire)|| string.IsNullOrEmpty(Empl.death) ||
string.IsNullOrEmpty(Empl.JobName) || string.IsNullOrEmpty(Empl.More) ||
string.IsNullOrEmpty(Empl.AndMore))
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
Any solution I've found so far to address this is time-consuming, and would require writing more code.
Is there any way to know which value is string.IsNullOrEmpty without having to change this IF statement too much? Worse-case, I can check every single statement separately, but I would prefer not doing this.
Thanks.
No, there's no "magic" function that will tell you which of a series of expression in an OR statement are true. Also, since you're using the short-circuiting version, the statement will return true after the first true condition, so the remaining expressions are not even evaluated.
However, you could do something like this:
bool[] checks = {
string.IsNullOrEmpty(Empl.Name) , string.IsNullOrEmpty(Empl.last) ,
string.IsNullOrEmpty(Empl.init) , string.IsNullOrEmpty(Empl.cat1) ,
string.IsNullOrEmpty(Empl.history) , string.IsNullOrEmpty(Empl.cat2) ,
string.IsNullOrEmpty(Empl.year) , string.IsNullOrEmpty(Empl.month) ,
string.IsNullOrEmpty(Empl.retire) , string.IsNullOrEmpty(Empl.spouse) ,
string.IsNullOrEmpty(Empl.children) , string.IsNullOrEmpty(Empl.bday) ,
string.IsNullOrEmpty(Empl.hire) , string.IsNullOrEmpty(Empl.death) ,
string.IsNullOrEmpty(Empl.JobName) , string.IsNullOrEmpty(Empl.More) ,
string.IsNullOrEmpty(Empl.AndMore)
};
if(checks.Any())
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
now the checks variable holds the result of each expression.
I find this sort of an more elegant way to use ModelState.isValid.
Some reference: What is ModelState.IsValid valid for in ASP.NET MVC in NerdDinner?
For your model, you can add following annotation:
[Required(AllowEmptyStrings= false)]
public string Boo { get; set; }
When you do validation, try:
if (!ModelState.IsValid)
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
Yes, write your own string extension method that does the same check, but also takes in a List and add the field name to the list. Declare the list of strings before the if and you will have a list of offending fields where your comment is.
This can be improved upon with a bit of reflection to automatically get the name and maybe make a few optimizations but it is on the right track.
Keep in mind that the first condition that violates the if statement will cause it to fail, so you will get an incomplete list (of one item) unless your if is constructed differently.
public static class StringExtensions
{
public static bool CheckIsNullOrEmptyAndListIt(this string field, string fieldName, List<string> naughties)
{
var result = String.IsNullOrEmpty(field);
if (result == true)
{
naughties.Add(fieldName);
}
return result;
}
}
}
using System.IO;
using System;
using System.Linq;
public class Program
{
public class Dog
{
public static string Name {get;set;}
public static string Race {get;set;}
}
public static bool validate(Dog dog)
{
bool val = true;
var y = dog.GetType()
.GetProperties()
.Select(p =>
{
object value =p.GetValue(dog,null);
if(string.IsNullOrEmpty(value.ToString())){ val=false; return false;}
else return true;
})
.ToArray();
return val;
}
public static void Main()
{
Dog dog= new Dog();
Dog.Name = "Peter";
Dog.Race = "";
if(validate(dog))
{
Console.WriteLine("Hello, World!");
}
}
}
You can use something like this :
public static class ValidationHelper
{
public static IEnumerable<string> FindEmptyProperties<T>(T target, params Expression<Func<T, string>>[] propertySelectors)
{
foreach (var propertySelector in propertySelectors)
{
if (string.IsNullOrEmpty(propertySelector.Compile()(target)))
{
var memberExpr = propertySelector.Body as MemberExpression;
yield return memberExpr.Member.Name;
}
}
}
}
Usage :
var failed = ValidationHelper.FindEmptyProperties(Empl, x => x.Name, x => x.last, x => x.init, x => x.cat1).ToList();
if (failed.Any())
{
throw new InvalidOperationException(
string.Format("Error: {0} is missing",
string.Join(", ", failed)));
}
If you use ASP.NET MVC maybe use DataAnnotations...
For the general c# context consider PostSharp aspect oriented library! Geat project!
Otherwise: Maybe a reflection solution using plain .NET ? (Created just for you! I think i keep for some own projects maybe)
Works with different types and you can control the targeted bindingflags.
Provides a common base class for your data transfer objects. (dto)
Reflection is performance optimized and working for generics as well!
public class Program
{
public void Main()
{
Empl test = new Empl()
{
TestProp = "blub",
TestInt = 1
};
if (test.ValidateProperties(Validations.CheckEmptyStringsAndZeroInts))
{
Console.WriteLine("validation passed");
}
else
{
Console.WriteLine("validation failed");
}
}
}
private static class Validations
{
//put this in a static class with standard checks
public static Func<object, bool> CheckEmptyStringsAndZeroInts = o =>
{
if (o is string && string.IsNullOrEmpty((string)o))
{
return false;
}
else if (o is int && ((int) o) == 0)
{
return false;
}
// ignore other property types
return true;
};
}
// Derive all your models like this. deriving from an Empl class is still valid and working!
//[IncludeBindingFlagsForPropertyReflctionAttribute(/*your custom binding flags*/)] //can also override the binding flags in derived classes!
public class Empl : DtoBase<Empl>
{
public string TestProp { get; set; }
public int TestInt { get; set; }
// Your properties here
}
// Helps you to control the targeted properties. you can filter for public or protected members for example
public class IncludeBindingFlagsForPropertyReflctionAttribute : Attribute
{
public BindingFlags BindingFlags { get; }
public IncludeBindingFlagsForPropertyReflctionAttribute(BindingFlags propertySearchBindingFlags)
{
BindingFlags = propertySearchBindingFlags;
}
}
//Looks much. But used once as base class can do those validations for you
[IncludeBindingFlagsForPropertyReflction(BindingFlags.Public | BindingFlags.Instance)]
public abstract class DtoBase<TDto> where TDto : DtoBase<TDto>
{
private static Dictionary<Type, List<PropertyInfo>> DtoPropertyInfosStorage { get; }
private List<PropertyInfo> DtoPropertyInfos => DtoPropertyInfosStorage[typeof (TDto)];
static DtoBase()
{
DtoPropertyInfosStorage = new Dictionary<Type, List<PropertyInfo>>();
Type tDto = typeof (TDto);
var includeBindingFlagsForProperty = GetAttribute(tDto);
BindingFlags defaultTargetFlags = BindingFlags.Instance | BindingFlags.Public;
DtoPropertyInfosStorage.Add(typeof(TDto), new List<PropertyInfo>(typeof(TDto).GetProperties(includeBindingFlagsForProperty?.BindingFlags ?? defaultTargetFlags)));
}
private static IncludeBindingFlagsForPropertyReflctionAttribute GetAttribute(Type dtoType)
{
bool stopRecursion = !dtoType.IsSubclassOf(typeof(DtoBase<TDto>));
var includeBindingFlagsForProperty = dtoType.GetCustomAttributes(typeof(IncludeBindingFlagsForPropertyReflctionAttribute)).FirstOrDefault();
if (includeBindingFlagsForProperty == null && !stopRecursion)
{
return GetAttribute(dtoType.BaseType);
}
return null;
}
/// <summary>
/// You can handle your validation type in you validation function yourself.
/// </summary>
public bool ValidateProperties(Func<object, bool> validationFunction)
{
foreach (KeyValuePair<Type, List<PropertyInfo>> dtoPropertyInfo in DtoPropertyInfosStorage)
{
foreach (PropertyInfo propertyInfo in DtoPropertyInfos)
{
if (!validationFunction(propertyInfo.))
{
return false;
}
}
}
return true;
}
/// <summary>
/// You can pass your targeted property type like string to TPropertyType
/// <![CDATA[ Example:
/// if(ValidateProperties<string>(validate => !string.IsNullOrEmpty(validate)))
/// {
/// properties not empty?
/// }
/// ]]]]>
/// </summary>
public bool ValidateProperties<TPropertyType>(Func<TPropertyType, bool> validationFunction)
{
List<PropertyInfo> targetPropertyInfos =
DtoPropertyInfos.Where(prop => prop.PropertyType == typeof (TPropertyType))
.ToList();
foreach (PropertyInfo dtoPropertyInfo in targetPropertyInfos)
{
if (validationFunction((TPropertyType) dtoPropertyInfo.GetValue(this)))
{
return false;
}
}
return true;
}
}
My question is shown in this code
I have class like that
public class MainCS
{
public int A;
public int B;
public int C;
public int D;
}
public class Sub1
{
public int A;
public int B;
public int C;
}
public void MethodA(Sub1 model)
{
MainCS mdata = new MainCS() { A = model.A, B = model.B, C = model.C };
// is there a way to directly cast class Sub1 into MainCS like that
mdata = (MainCS) model;
}
Use JSON serialization and deserialization:
using Newtonsoft.Json;
Class1 obj1 = new Class1();
Class2 obj2 = JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj1));
Or:
public class Class1
{
public static explicit operator Class2(Class1 obj)
{
return JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj));
}
}
Which then allows you to do something like
Class1 obj1 = new Class1();
Class2 obj2 = (Class2)obj1;
You have already defined the conversion, you just need to take it one step further if you would like to be able to cast. For example:
public class sub1
{
public int a;
public int b;
public int c;
public static explicit operator maincs(sub1 obj)
{
maincs output = new maincs() { a = obj.a, b = obj.b, c = obj.c };
return output;
}
}
Which then allows you to do something like
static void Main()
{
sub1 mySub = new sub1();
maincs myMain = (maincs)mySub;
}
What he wants to say is:
"If you have two classes which share most of the same properties you can cast an object from class a to class b and automatically make the system understand the assignment via the shared property names?"
Option 1: Use reflection
Disadvantage : It's gonna slow you down more than you think.
Option 2: Make one class derive from another, the first one with common properties and other an extension of that.
Disadvantage: Coupled! if your're doing that for two layers in your application then the two layers will be coupled!
Let there be:
class customer
{
public string firstname { get; set; }
public string lastname { get; set; }
public int age { get; set; }
}
class employee
{
public string firstname { get; set; }
public int age { get; set; }
}
Now here is an extension for Object type:
public static T Cast<T>(this Object myobj)
{
Type objectType = myobj.GetType();
Type target = typeof(T);
var x = Activator.CreateInstance(target, false);
var z = from source in objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property select source ;
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property select source;
List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
value = myobj.GetType().GetProperty(memberInfo.Name).GetValue(myobj,null);
propertyInfo.SetValue(x,value,null);
}
return (T)x;
}
Now you use it like this:
static void Main(string[] args)
{
var cus = new customer();
cus.firstname = "John";
cus.age = 3;
employee emp = cus.Cast<employee>();
}
Method cast checks common properties between two objects and does the assignment automatically.
You could change your class structure to:
public class maincs : sub1
{
public int d;
}
public class sub1
{
public int a;
public int b;
public int c;
}
Then you could keep a list of sub1 and cast some of them to mainc.
By using following code you can copy any class object to another class object for same name and same type of properties.
public class CopyClass
{
/// <summary>
/// Copy an object to destination object, only matching fields will be copied
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sourceObject">An object with matching fields of the destination object</param>
/// <param name="destObject">Destination object, must already be created</param>
public static void CopyObject<T>(object sourceObject, ref T destObject)
{
// If either the source, or destination is null, return
if (sourceObject == null || destObject == null)
return;
// Get the type of each object
Type sourceType = sourceObject.GetType();
Type targetType = destObject.GetType();
// Loop through the source properties
foreach (PropertyInfo p in sourceType.GetProperties())
{
// Get the matching property in the destination object
PropertyInfo targetObj = targetType.GetProperty(p.Name);
// If there is none, skip
if (targetObj == null)
continue;
// Set the value in the destination
targetObj.SetValue(destObject, p.GetValue(sourceObject, null), null);
}
}
}
Call Method Like,
ClassA objA = new ClassA();
ClassB objB = new ClassB();
CopyClass.CopyObject(objOfferMast, ref objB);
It will copy objA into objB.
You can provide an explicit overload for the cast operator:
public static explicit operator maincs(sub1 val)
{
var ret = new maincs() { a = val.a, b = val.b, c = val.c };
return ret;
}
Another option would be to use an interface that has the a, b, and c properties and implement the interface on both of the classes. Then just have the parameter type to methoda be the interface instead of the class.
Using this code you can copy any class object to another class object for same name and same type of properties.
JavaScriptSerializer JsonConvert = new JavaScriptSerializer();
string serializeString = JsonConvert.Serialize(objectEntity);
objectViewModel objVM = JsonConvert.Deserialize<objectViewModel>(serializeString);
There are some great answers here, I just wanted to add a little bit of type checking here as we cannot assume that if properties exist with the same name, that they are of the same type. Here is my offering, which extends on the previous, very excellent answer as I had a few little glitches with it.
In this version I have allowed for the consumer to specify fields to be excluded, and also by default to exclude any database / model specific related properties.
public static T Transform<T>(this object myobj, string excludeFields = null)
{
// Compose a list of unwanted members
if (string.IsNullOrWhiteSpace(excludeFields))
excludeFields = string.Empty;
excludeFields = !string.IsNullOrEmpty(excludeFields) ? excludeFields + "," : excludeFields;
excludeFields += $"{nameof(DBTable.ID)},{nameof(DBTable.InstanceID)},{nameof(AuditableBase.CreatedBy)},{nameof(AuditableBase.CreatedByID)},{nameof(AuditableBase.CreatedOn)}";
var objectType = myobj.GetType();
var targetType = typeof(T);
var targetInstance = Activator.CreateInstance(targetType, false);
// Find common members by name
var sourceMembers = from source in objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var targetMembers = from source in targetType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var commonMembers = targetMembers.Where(memberInfo => sourceMembers.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
// Remove unwanted members
commonMembers.RemoveWhere(x => x.Name.InList(excludeFields));
foreach (var memberInfo in commonMembers)
{
if (!((PropertyInfo)memberInfo).CanWrite) continue;
var targetProperty = typeof(T).GetProperty(memberInfo.Name);
if (targetProperty == null) continue;
var sourceProperty = myobj.GetType().GetProperty(memberInfo.Name);
if (sourceProperty == null) continue;
// Check source and target types are the same
if (sourceProperty.PropertyType.Name != targetProperty.PropertyType.Name) continue;
var value = myobj.GetType().GetProperty(memberInfo.Name)?.GetValue(myobj, null);
if (value == null) continue;
// Set the value
targetProperty.SetValue(targetInstance, value, null);
}
return (T)targetInstance;
}
I tried to use the Cast Extension (see https://stackoverflow.com/users/247402/stacker) in a situation where the Target Type contains a Property that is not present in the Source Type. It did not work, I'm not sure why. I refactored to the following extension that did work for my situation:
public static T Casting<T>(this Object source)
{
Type sourceType = source.GetType();
Type targetType = typeof(T);
var target = Activator.CreateInstance(targetType, false);
var sourceMembers = sourceType.GetMembers()
.Where(x => x.MemberType == MemberTypes.Property)
.ToList();
var targetMembers = targetType.GetMembers()
.Where(x => x.MemberType == MemberTypes.Property)
.ToList();
var members = targetMembers
.Where(x => sourceMembers
.Select(y => y.Name)
.Contains(x.Name));
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
value = source.GetType().GetProperty(memberInfo.Name).GetValue(source, null);
propertyInfo.SetValue(target, value, null);
}
return (T)target;
}
Note that I changed the name of the extension as the Name Cast conflicts with results from Linq. Hat tip https://stackoverflow.com/users/2093880/usefulbee
var obj = _account.Retrieve(Email, hash);
AccountInfoResponse accountInfoResponse = new AccountInfoResponse();
if (obj != null)
{
accountInfoResponse =
JsonConvert.
DeserializeObject<AccountInfoResponse>
(JsonConvert.SerializeObject(obj));
}
image description
I developed a Class ObjectChanger that contains the functions ConvertToJson, DeleteFromJson, AddToJson, and ConvertToObject. These functions can be used to convert a C# object to JSON which properties can then be removed or added accordingly. Afterwards the adjusted JSON object can simply be converted to a new object using ConvertToObject function. In the sample code below the class "AtoB" utilizes ObjectChanger in its GetAtoB() function:
using System.Collections.Generic;
using Newtonsoft.Json;
using Nancy.Json;
namespace YourNameSpace
{
public class A
{
public int num1 { get; set; }
public int num2 { get; set; }
public int num3 { get; set; }
}
public class B//remove num2 and add num4
{
public int num1 { get; set; }
public int num3 { get; set; }
public int num4 { get; set; }
}
/// <summary>
/// This class utilizes ObjectChanger to illustrate how
/// to convert object of type A to one of type B
/// by converting A to a Json Object, manipulating the JSON
/// and then converting it to object of type B
/// </summary>
public class AtoB
{
public dynamic GetAtoB()
{
A objectA = new A
{
num1 =1, num2 =2,num3 =3
};
//convert "objectA" to JSON object "jsonA"
dynamic jsonA = ObjectChanger.ConvertToJson(objectA);
//remove num2 from jsonA
ObjectChanger.DeleteFromJson(jsonA, "num2");
//add property num4 with value 4 to jsonA
ObjectChanger.AddToJson(jsonA, "num4", 4);
B objectB = ObjectChanger.ConvertToObject<B>(jsonA);
return objectB;
//note: Above DeleteFromJson not needed if the
//property(e.g "num2") doesn't exist in objectB
//the jsonA will still keep the num2 but when
//ConvertToObject is called the objectB will only get
//populated with the relevant fields.
}
}
public class ObjectChanger
{
/// <summary>
/// Converts a provided class to JsonObject
/// sample use: dynamic r = ObjectChanger.ConvertToJson(providedObj);
/// </summary>
public static dynamic ConvertToJson(dynamic providedObj)
{
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//https://stackoverflow.com/questions/7397207/json-net-error-self-referencing-loop-detected-for-type
return JsonConvert.DeserializeObject<System.Dynamic.ExpandoObject>
(JsonConvert.SerializeObject(providedObj,jss));
}
/// <summary>
/// Deletes Property from Json Object
/// sample use: dynamic r = ObjectChanger.ConvertToJson(providedObj);
/// ((IDictionary<string, object>)r).Remove("keyvalue");
/// </summary>
public static dynamic DeleteFromJson(dynamic providedObj, string keyvalue)
{
((IDictionary<string, object>)providedObj).Remove(keyvalue);
return providedObj;
}
/// <summary>
/// Adds Property to provided Json Object
/// </summary>
/// <param name="providedObj"></param>
/// <param name="key"></param>
/// <param name="keyvalue"></param>
/// <returns>Returns updated Object</returns>
public static dynamic AddToJson(dynamic providedObj, string key,
dynamic keyvalue)
{
((IDictionary<string, object>)providedObj).Add(key, keyvalue);
return providedObj;
}
/// <summary>
/// Converts provided object providedObj
/// to an object of type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="providedObj"></param>
/// <returns></returns>
public static T ConvertToObject<T>(dynamic providedObj)
{
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(providedObj);
var c = serializer.Deserialize<T>(json);
return c;
}
}
}
Closed. This question is opinion-based. It is not currently accepting answers.
Closed 4 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
This is what I've come up with as a method on a class inherited by many of my other classes. The idea is that it allows the simple comparison between properties of Objects of the same Type.
Now, this does work - but in the interest of improving the quality of my code I thought I'd throw it out for scrutiny. How can it be better/more efficient/etc.?
/// <summary>
/// Compare property values (as strings)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public bool PropertiesEqual(object comparisonObject)
{
Type sourceType = this.GetType();
Type destinationType = comparisonObject.GetType();
if (sourceType == destinationType)
{
PropertyInfo[] sourceProperties = sourceType.GetProperties();
foreach (PropertyInfo pi in sourceProperties)
{
if ((sourceType.GetProperty(pi.Name).GetValue(this, null) == null && destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null) == null))
{
// if both are null, don't try to compare (throws exception)
}
else if (!(sourceType.GetProperty(pi.Name).GetValue(this, null).ToString() == destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null).ToString()))
{
// only need one property to be different to fail Equals.
return false;
}
}
}
else
{
throw new ArgumentException("Comparison object must be of the same type.","comparisonObject");
}
return true;
}
I was looking for a snippet of code that would do something similar to help with writing unit test. Here is what I ended up using.
public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(ignore);
foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
{
if (!ignoreList.Contains(pi.Name))
{
object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
object toValue = type.GetProperty(pi.Name).GetValue(to, null);
if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
{
return false;
}
}
}
return true;
}
return self == to;
}
EDIT:
Same code as above but uses LINQ and Extension methods :
public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = typeof(T);
var ignoreList = new List<string>(ignore);
var unequalProperties =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name) && pi.GetUnderlyingType().IsSimpleType() && pi.GetIndexParameters().Length == 0
let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
let toValue = type.GetProperty(pi.Name).GetValue(to, null)
where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
select selfValue;
return !unequalProperties.Any();
}
return self == to;
}
public static class TypeExtensions
{
/// <summary>
/// Determine whether a type is simple (String, Decimal, DateTime, etc)
/// or complex (i.e. custom class with public properties and methods).
/// </summary>
/// <see cref="http://stackoverflow.com/questions/2442534/how-to-test-if-type-is-primitive"/>
public static bool IsSimpleType(
this Type type)
{
return
type.IsValueType ||
type.IsPrimitive ||
new[]
{
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
(Convert.GetTypeCode(type) != TypeCode.Object);
}
public static Type GetUnderlyingType(this MemberInfo member)
{
switch (member.MemberType)
{
case MemberTypes.Event:
return ((EventInfo)member).EventHandlerType;
case MemberTypes.Field:
return ((FieldInfo)member).FieldType;
case MemberTypes.Method:
return ((MethodInfo)member).ReturnType;
case MemberTypes.Property:
return ((PropertyInfo)member).PropertyType;
default:
throw new ArgumentException
(
"Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
);
}
}
}
UPDATE: The latest version of Compare-Net-Objects is located on GitHub , has NuGet package and Tutorial. It can be called like
//This is the comparison class
CompareLogic compareLogic = new CompareLogic();
ComparisonResult result = compareLogic.Compare(person1, person2);
//These will be different, write out the differences
if (!result.AreEqual)
Console.WriteLine(result.DifferencesString);
Or if you need to change some configuration, use
CompareLogic basicComparison = new CompareLogic()
{ Config = new ComparisonConfig()
{ MaxDifferences = propertyCount
//add other configurations
}
};
Full list of configurable parameters is in ComparisonConfig.cs
Original answer:
The limitations I see in your code:
The biggest one is that it doesn't do a deep object comparison.
It doesn't do an element by element comparison in case properties are lists or contain lists as elements (this can go n-levels).
It doesn't take into account that some type of properties should not be compared (e.g. a Func property used for filtering purposes, like the one in the PagedCollectionView class).
It doesn't keep track of what properties actually were different (so you can show in your assertions).
I was looking today for some solution for unit-testing purposes to do property by property deep comparison and I ended up using: http://comparenetobjects.codeplex.com.
It is a free library with just one class which you can simply use like this:
var compareObjects = new CompareObjects()
{
CompareChildren = true, //this turns deep compare one, otherwise it's shallow
CompareFields = false,
CompareReadOnly = true,
ComparePrivateFields = false,
ComparePrivateProperties = false,
CompareProperties = true,
MaxDifferences = 1,
ElementsToIgnore = new List<string>() { "Filter" }
};
Assert.IsTrue(
compareObjects.Compare(objectA, objectB),
compareObjects.DifferencesString
);
Also, it can be easily re-compiled for Silverlight. Just copy the one class into a Silverlight project and remove one or two lines of code for comparisons that are not available in Silverlight, like private members comparison.
I think it would be best to follow the pattern for Override Object#Equals()
For a better description: Read Bill Wagner's Effective C# - Item 9 I think
public override Equals(object obOther)
{
if (null == obOther)
return false;
if (object.ReferenceEquals(this, obOther)
return true;
if (this.GetType() != obOther.GetType())
return false;
# private method to compare members.
return CompareMembers(this, obOther as ThisClass);
}
Also in methods that check for equality, you should return either true or false. either they are equal or they are not.. instead of throwing an exception, return false.
I'd consider overriding Object#Equals.
Even though you must have considered this, using Reflection to compare properties is supposedly slow (I dont have numbers to back this up). This is the default behavior for valueType#Equals in C# and it is recommended that you override Equals for value types and do a member wise compare for performance. (Earlier I speed-read this as you have a collection of custom Property objects... my bad.)
Update-Dec 2011:
Of course, if the type already has a production Equals() then you need another approach.
If you're using this to compare immutable data structures exclusively for test purposes, you shouldn't add an Equals to production classes (Someone might hose the tests by chainging the Equals implementation or you may prevent creation of a production-required Equals implementation).
If performance doesn't matter, you could serialize them and compare the results:
var serializer = new XmlSerializer(typeof(TheObjectType));
StringWriter serialized1 = new StringWriter(), serialized2 = new StringWriter();
serializer.Serialize(serialized1, obj1);
serializer.Serialize(serialized2, obj2);
bool areEqual = serialized1.ToString() == serialized2.ToString();
I think the answer of Big T was quite good but the deep comparison was missing, so I tweaked it a little bit:
using System.Collections.Generic;
using System.Reflection;
/// <summary>Comparison class.</summary>
public static class Compare
{
/// <summary>Compare the public instance properties. Uses deep comparison.</summary>
/// <param name="self">The reference object.</param>
/// <param name="to">The object to compare.</param>
/// <param name="ignore">Ignore property with name.</param>
/// <typeparam name="T">Type of objects.</typeparam>
/// <returns><see cref="bool">True</see> if both objects are equal, else <see cref="bool">false</see>.</returns>
public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = self.GetType();
var ignoreList = new List<string>(ignore);
foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (ignoreList.Contains(pi.Name))
{
continue;
}
var selfValue = type.GetProperty(pi.Name).GetValue(self, null);
var toValue = type.GetProperty(pi.Name).GetValue(to, null);
if (pi.PropertyType.IsClass && !pi.PropertyType.Module.ScopeName.Equals("CommonLanguageRuntimeLibrary"))
{
// Check of "CommonLanguageRuntimeLibrary" is needed because string is also a class
if (PublicInstancePropertiesEqual(selfValue, toValue, ignore))
{
continue;
}
return false;
}
if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
{
return false;
}
}
return true;
}
return self == to;
}
}
I would add the following line to the PublicInstancePropertiesEqual method to avoid copy & paste errors:
Assert.AreNotSame(self, to);
Do you override .ToString() on all of your objects that are in the properties? Otherwise, that second comparison could come back with null.
Also, in that second comparison, I'm on the fence about the construct of !( A == B) compared to (A != B), in terms of readability six months/two years from now. The line itself is pretty wide, which is ok if you've got a wide monitor, but might not print out very well. (nitpick)
Are all of your objects always using properties such that this code will work? Could there be some internal, non-propertied data that could be different from one object to another, but all exposed data is the same? I'm thinking of some data which could change over time, like two random number generators that happen to hit the same number at one point, but are going to produce two different sequences of information, or just any data that doesn't get exposed through the property interface.
If you are only comparing objects of the same type or further down the inheritance chain, why not specify the parameter as your base type, rather than object ?
Also do null checks on the parameter as well.
Furthermore I'd make use of 'var' just to make the code more readable (if its c#3 code)
Also, if the object has reference types as properties then you are just calling ToString() on them which doesn't really compare values. If ToString isn't overwridden then its just going to return the type name as a string which could return false-positives.
The first thing I would suggest would be to split up the actual comparison so that it's a bit more readable (I've also taken out the ToString() - is that needed?):
else {
object originalProperty = sourceType.GetProperty(pi.Name).GetValue(this, null);
object comparisonProperty = destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null);
if (originalProperty != comparisonProperty)
return false;
The next suggestion would be to minimise the use of reflection as much as possible - it's really slow. I mean, really slow. If you are going to do this, I would suggest caching the property references. I'm not intimately familiar with the Reflection API, so if this is a bit off, just adjust to make it compile:
// elsewhere
Dictionary<object, Property[]> lookupDictionary = new Dictionary<object, Property[]>;
Property[] objectProperties = null;
if (lookupDictionary.ContainsKey(sourceType)) {
objectProperties = lookupProperties[sourceType];
} else {
// build array of Property references
PropertyInfo[] sourcePropertyInfos = sourceType.GetProperties();
Property[] sourceProperties = new Property[sourcePropertyInfos.length];
for (int i=0; i < sourcePropertyInfos.length; i++) {
sourceProperties[i] = sourceType.GetProperty(pi.Name);
}
// add to cache
objectProperties = sourceProperties;
lookupDictionary[object] = sourceProperties;
}
// loop through and compare against the instances
However, I have to say that I agree with the other posters. This smells lazy and inefficient. You should be implementing IComparable instead :-).
here is revised one to treat null = null as equal
private bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(ignore);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!ignoreList.Contains(pi.Name))
{
object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
object toValue = type.GetProperty(pi.Name).GetValue(to, null);
if (selfValue != null)
{
if (!selfValue.Equals(toValue))
return false;
}
else if (toValue != null)
return false;
}
}
return true;
}
return self == to;
}
I ended up doing this:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b)
{
int count = a.GetType().GetProperties().Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe();
bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb)
{
return false;
}
}
return true;
}
Usage:
if (Compare<ObjectType>(a, b))
Update
If you want to ignore some properties by name:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b, params string[] ignore)
{
int count = a.GetType().GetProperties().Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = a.GetType().GetProperties()[i].GetValue(a, null).ToStringNullSafe();
bb = b.GetType().GetProperties()[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb && ignore.Where(x => x == a.GetType().GetProperties()[i].Name).Count() == 0)
{
return false;
}
}
return true;
}
Usage:
if (MyFunction.Compare<ObjType>(a, b, "Id","AnotherProp"))
You can optimize your code by calling GetProperties only once per type:
public static string ToStringNullSafe(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
public static bool Compare<T>(T a, T b, params string[] ignore)
{
var aProps = a.GetType().GetProperties();
var bProps = b.GetType().GetProperties();
int count = aProps.Count();
string aa, bb;
for (int i = 0; i < count; i++)
{
aa = aProps[i].GetValue(a, null).ToStringNullSafe();
bb = bProps[i].GetValue(b, null).ToStringNullSafe();
if (aa != bb && ignore.Where(x => x == aProps[i].Name).Count() == 0)
{
return false;
}
}
return true;
}
For completeness I want to add reference to
http://www.cyotek.com/blog/comparing-the-properties-of-two-objects-via-reflection
It has more complete logic than most of others answers on this page.
However I prefer Compare-Net-Objects library
https://github.com/GregFinzer/Compare-Net-Objects (referred by Liviu Trifoi's answer)
The library has NuGet package http://www.nuget.org/packages/CompareNETObjects and multiple options to configure.
Make sure objects aren't null.
Having obj1 and obj2:
if(obj1 == null )
{
return false;
}
return obj1.Equals( obj2 );
This works even if the objects are different. you could customize the methods in the utilities class maybe you want to compare private properties as well...
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;
}
}
Update on Liviu's answer above - CompareObjects.DifferencesString has been deprecated.
This works well in a unit test:
CompareLogic compareLogic = new CompareLogic();
ComparisonResult result = compareLogic.Compare(object1, object2);
Assert.IsTrue(result.AreEqual);
This method will get properties of the class and compare the values for each property. If any of the values are different, it will return false, else it will return true.
public static bool Compare<T>(T Object1, T object2)
{
//Get the type of the object
Type type = typeof(T);
//return false if any of the object is false
if (Object1 == null || object2 == null)
return false;
//Loop through each properties inside class and get values for the property from both the objects and compare
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
if (property.Name != "ExtensionData")
{
string Object1Value = string.Empty;
string Object2Value = string.Empty;
if (type.GetProperty(property.Name).GetValue(Object1, null) != null)
Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString();
if (type.GetProperty(property.Name).GetValue(object2, null) != null)
Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString();
if (Object1Value.Trim() != Object2Value.Trim())
{
return false;
}
}
}
return true;
}
Usage:
bool isEqual = Compare<Employee>(Object1, Object2)
To expand on #nawfal:s answer, I use this to test objects of different types in my unit tests to compare equal property names. In my case database entity and DTO.
Used like this in my test;
Assert.IsTrue(resultDto.PublicInstancePropertiesEqual(expectedEntity));
public static bool PublicInstancePropertiesEqual<T, Z>(this T self, Z to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = typeof(T);
var type2 = typeof(Z);
var ignoreList = new List<string>(ignore);
var unequalProperties =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name)
let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
let toValue = type2.GetProperty(pi.Name).GetValue(to, null)
where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
select selfValue;
return !unequalProperties.Any();
}
return self == null && to == null;
}
sometimes you don't want to compare all public properties and want to compare only the subset of them, so in this case you can just move logic to compare the desired list of properties to abstract class
public abstract class ValueObject<T> where T : ValueObject<T>
{
protected abstract IEnumerable<object> GetAttributesToIncludeInEqualityCheck();
public override bool Equals(object other)
{
return Equals(other as T);
}
public bool Equals(T other)
{
if (other == null)
{
return false;
}
return GetAttributesToIncludeInEqualityCheck()
.SequenceEqual(other.GetAttributesToIncludeInEqualityCheck());
}
public static bool operator ==(ValueObject<T> left, ValueObject<T> right)
{
return Equals(left, right);
}
public static bool operator !=(ValueObject<T> left, ValueObject<T> right)
{
return !(left == right);
}
public override int GetHashCode()
{
int hash = 17;
foreach (var obj in this.GetAttributesToIncludeInEqualityCheck())
hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode());
return hash;
}
}
and use this abstract class later to compare the objects
public class Meters : ValueObject<Meters>
{
...
protected decimal DistanceInMeters { get; private set; }
...
protected override IEnumerable<object> GetAttributesToIncludeInEqualityCheck()
{
return new List<Object> { DistanceInMeters };
}
}
my solution inspired from Aras Alenin answer above where I added one level of object comparison and a custom object for comparison results. I am also interested to get property name with object name:
public static IEnumerable<ObjectPropertyChanged> GetPublicSimplePropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored) where T : class
{
return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, true, null, null);
}
public static IReadOnlyList<ObjectPropertyChanged> GetPublicGenericPropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored) where T : class
{
return GetPublicGenericPropertiesChanged(previous, proposedChange, namesOfPropertiesToBeIgnored, false, null, null);
}
/// <summary>
/// Gets the names of the public properties which values differs between first and second objects.
/// Considers 'simple' properties AND for complex properties without index, get the simple properties of the children objects.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="previous">The previous object.</param>
/// <param name="proposedChange">The second object which should be the new one.</param>
/// <param name="namesOfPropertiesToBeIgnored">The names of the properties to be ignored.</param>
/// <param name="simpleTypeOnly">if set to <c>true</c> consider simple types only.</param>
/// <param name="parentTypeString">The parent type string. Meant only for recursive call with simpleTypeOnly set to <c>true</c>.</param>
/// <param name="secondType">when calling recursively, the current type of T must be clearly defined here, as T will be more generic (using base class).</param>
/// <returns>
/// the names of the properties
/// </returns>
private static IReadOnlyList<ObjectPropertyChanged> GetPublicGenericPropertiesChanged<T>(this T previous, T proposedChange,
string[] namesOfPropertiesToBeIgnored, bool simpleTypeOnly, string parentTypeString, Type secondType) where T : class
{
List<ObjectPropertyChanged> propertiesChanged = new List<ObjectPropertyChanged>();
if (previous != null && proposedChange != null)
{
var type = secondType == null ? typeof(T) : secondType;
string typeStr = parentTypeString + type.Name + ".";
var ignoreList = namesOfPropertiesToBeIgnored.CreateList();
IEnumerable<IEnumerable<ObjectPropertyChanged>> genericPropertiesChanged =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name) && pi.GetIndexParameters().Length == 0
&& (!simpleTypeOnly || simpleTypeOnly && pi.PropertyType.IsSimpleType())
let firstValue = type.GetProperty(pi.Name).GetValue(previous, null)
let secondValue = type.GetProperty(pi.Name).GetValue(proposedChange, null)
where firstValue != secondValue && (firstValue == null || !firstValue.Equals(secondValue))
let subPropertiesChanged = simpleTypeOnly || pi.PropertyType.IsSimpleType()
? null
: GetPublicGenericPropertiesChanged(firstValue, secondValue, namesOfPropertiesToBeIgnored, true, typeStr, pi.PropertyType)
let objectPropertiesChanged = subPropertiesChanged != null && subPropertiesChanged.Count() > 0
? subPropertiesChanged
: (new ObjectPropertyChanged(proposedChange.ToString(), typeStr + pi.Name, firstValue.ToStringOrNull(), secondValue.ToStringOrNull())).CreateList()
select objectPropertiesChanged;
if (genericPropertiesChanged != null)
{ // get items from sub lists
genericPropertiesChanged.ForEach(a => propertiesChanged.AddRange(a));
}
}
return propertiesChanged;
}
Using the following class to store comparison results
[System.Serializable]
public class ObjectPropertyChanged
{
public ObjectPropertyChanged(string objectId, string propertyName, string previousValue, string changedValue)
{
ObjectId = objectId;
PropertyName = propertyName;
PreviousValue = previousValue;
ProposedChangedValue = changedValue;
}
public string ObjectId { get; set; }
public string PropertyName { get; set; }
public string PreviousValue { get; set; }
public string ProposedChangedValue { get; set; }
}
And a sample unit test:
[TestMethod()]
public void GetPublicGenericPropertiesChangedTest1()
{
// Define objects to test
Function func1 = new Function { Id = 1, Description = "func1" };
Function func2 = new Function { Id = 2, Description = "func2" };
FunctionAssignment funcAss1 = new FunctionAssignment
{
Function = func1,
Level = 1
};
FunctionAssignment funcAss2 = new FunctionAssignment
{
Function = func2,
Level = 2
};
// Main test: read properties changed
var propertiesChanged = Utils.GetPublicGenericPropertiesChanged(funcAss1, funcAss2, null);
Assert.IsNotNull(propertiesChanged);
Assert.IsTrue(propertiesChanged.Count == 3);
Assert.IsTrue(propertiesChanged[0].PropertyName == "FunctionAssignment.Function.Description");
Assert.IsTrue(propertiesChanged[1].PropertyName == "FunctionAssignment.Function.Id");
Assert.IsTrue(propertiesChanged[2].PropertyName == "FunctionAssignment.Level");
}