C# Generic Objects Compareing - c#

This is generic class to compare two objects with a user defined property, but it doesn't work correctly.
public class ObjectComparer<T> : IEqualityComparer<T>
{
private string _propertyName;
private string PropertyName
{
get { return _propertyName; }
set
{
_propertyName = value;
PropertyInfo = typeof(T).GetProperty(PropertyName);
}
}
public ObjectComparer(string propertyName)
{
PropertyName = propertyName;
}
private PropertyInfo PropertyInfo { get; set; }
public int GetHashCode(T type)
{
if (type== null)
{
return 0;
}
return PropertyInfo.GetHashCode();
}
public bool Equals(T obj1, T obj2)
{
if (ReferenceEquals(obj1, obj2))
{
return true;
}
if (ReferenceEquals(obj1, null) || ReferenceEquals(obj2, null))
{
return false;
}
return PropertyInfo.GetValue(obj1, null) == PropertyInfo.GetValue(obj2, null);
}
}
Issue 1)type == null (Possible compare of value type with null)
Issue 2)Use ReferenceEquals in this position is correct?
Update
"Word" class:
public class Word
{
public string EnglishText { get; set; }
public string LocalText { get; set; }
public string EditedText { get; set; }
}
Usage :
var except = Program.Localizer.DefaultLanguage.Words.Except(CurrentSelectedLanguage.Words, new ObjectComparer<Word>("EnglishText"));
Number of different objects based on the "EnglishName" is not correct.
Class authentic and Modified below.
Special thanks to Sriram Sakthivel
public class ObjectComparer<T> : IEqualityComparer<T>
{
private string _propertyName;
private string PropertyName
{
get { return _propertyName; }
set
{
_propertyName = value;
PropertyInfo = typeof(T).GetProperty(PropertyName);
}
}
public ObjectComparer(string propertyName)
{
PropertyName = propertyName;
}
private PropertyInfo PropertyInfo { get; set; }
public int GetHashCode(T obj)
{
if (ReferenceEquals(obj, null))
{
return 0;
}
return PropertyInfo.GetHashCode();
}
public bool Equals(T obj1, T obj2)
{
if (ReferenceEquals(obj1, obj2))
{
return true;
}
if (ReferenceEquals(obj1, null) || ReferenceEquals(obj2, null))
{
return false;
}
return Equals(PropertyInfo.GetValue(obj1, null),
PropertyInfo.GetValue(obj2, null));
}
}

Your GetHashCode is wrong. You're calling PropertyInfo.GetHashCode It should be using the Property value instead.
public int GetHashCode(T obj)
{
if (object.ReferenceEquals(obj, null))
{
return 0;
}
var value = PropertyInfo.GetValue(obj);
return value == null ? 0 : value.GetHashCode();
}
Also you can get rid of Possible compare of value type with null warning from resharper using object.ReferenceEquals
GetHashCode parameter is named as type, which is misleading, So I renamed it to obj
Update:
If you need to use value comparison you must use Object.Equals method as opposed to using == operator for object type
public bool Equals(T obj1, T obj2)
{
if (ReferenceEquals(obj1, obj2))
{
return true;
}
if (ReferenceEquals(obj1, null) || ReferenceEquals(obj2, null))
{
return false;
}
return object.Equals(PropertyInfo.GetValue(obj1, null),
PropertyInfo.GetValue(obj2, null));
}

Related

How to ignore case sensitive for Enum Data Type in Data Annotations in .NET

I am working on REST API. I have JSON input in POST request contains many parameters of which one is user language.
JSON:
{
"userlanguage":"en"
}
I have created Enum class for all Languages.
Note: For reference I provided English, Hindi languages only.
public enum Language
{
EN = 0,
HI =1
}
To validate Class, I am using EnumDataType and Required data annotations.
public class UserDetails
{
[Required]
[EnumDataType(typeof(Language), ErrorMessage = "Invalid Language Supported.")]
[JsonProperty("userlanguage")]
public string? UserLanguage { get; set; }
}
I created Extension class to validate any class models.
public static class HttpRequestValidationExtension
{
public static bool IsValid(this object o, out ICollection<ValidationResult> validationResults)
{
validationResults = new List<ValidationResult>();
return Validator.TryValidateObject(o, new ValidationContext(o, null, null), validationResults, true);
}
}
Usage in Controller Class:
UserDetails userDetails= JsonConvert.DeserializeObject<UserDetails>(requestBody);
if (!userDetails.IsValid(validationResults: out var validationResults))
{
string errorMessage = "Bad Request.";
int statusCode = 400;
return DisplayErrorMessage
(statusCode,
string.Join("", validationResults.Select(s => s.ErrorMessage).FirstOrDefault()),
functionName,
string.Join("", validationResults.Select(s => s.MemberNames).FirstOrDefault()));
}
Problem:
If I supply EN my validation is SUCCESS. If I supply en my validation is FIALED.
Is there any way to ignore case?
After checked the source code of EnumDataType I think you could implement a custom EnumDataType to archive what you want.
So I change the EnumDataType to EnumIgnoreCaseDataTypeAttribute
public class UserDetails
{
[Required]
[EnumIgnoreCaseDataTypeAttribute(typeof(Language), ErrorMessage = "Invalid Language Supported.")]
[JsonProperty("userlanguage")]
public string? UserLanguage { get; set; }
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = false)]
public class EnumIgnoreCaseDataTypeAttribute : DataTypeAttribute
{
public Type EnumType { get; private set; }
public EnumIgnoreCaseDataTypeAttribute(Type enumType)
: base("Enumeration")
{
this.EnumType = enumType;
}
public override bool IsValid(object value)
{
if (this.EnumType == null)
{
throw new InvalidOperationException();
}
if (!this.EnumType.IsEnum)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "", this.EnumType.FullName));
}
if (value == null)
{
return true;
}
string stringValue = value as string;
if (stringValue != null && String.IsNullOrEmpty(stringValue))
{
return true;
}
Type valueType = value.GetType();
if (valueType.IsEnum && this.EnumType != valueType)
{
// don't match a different enum that might map to the same underlying integer
//
return false;
}
if (!valueType.IsValueType && valueType != typeof(string))
{
// non-value types cannot be converted
return false;
}
if (valueType == typeof(bool) ||
valueType == typeof(float) ||
valueType == typeof(double) ||
valueType == typeof(decimal) ||
valueType == typeof(char))
{
// non-integral types cannot be converted
return false;
}
object convertedValue;
if (valueType.IsEnum)
{
Debug.Assert(valueType == value.GetType(), "The valueType should equal the Type of the value");
convertedValue = value;
}
else
{
try
{
if (stringValue != null)
{
convertedValue = Enum.Parse(this.EnumType, stringValue, true);
}
else
{
convertedValue = Enum.ToObject(this.EnumType, value);
}
}
catch (ArgumentException)
{
//
return false;
}
}
if (IsEnumTypeInFlagsMode(this.EnumType))
{
string underlying = GetUnderlyingTypeValueString(this.EnumType, convertedValue);
string converted = convertedValue.ToString();
return !underlying.Equals(converted);
}
else
{
return Enum.IsDefined(this.EnumType, convertedValue);
}
}
private static bool IsEnumTypeInFlagsMode(Type enumType)
{
return enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Length != 0;
}
private static string GetUnderlyingTypeValueString(Type enumType, object enumValue)
{
return Convert.ChangeType(enumValue, Enum.GetUnderlyingType(enumType), CultureInfo.InvariantCulture).ToString();
}
}
Source code reference: https://referencesource.microsoft.com/#System.ComponentModel.DataAnnotations/DataAnnotations/EnumDataTypeAttribute.cs,4c23e1f3dcd51167

Trying to Serialize a Type to XML, I Recieve Error "System.RuntimeType is inaccessible due to its protection level."

I found the following class in this question https://stackoverflow.com/a/9809872/2226152.
// a version of System.Type that can be serialized
[DataContract]
public class SerializableType
{
public Type type;
// when serializing, store as a string
[DataMember]
string TypeString
{
get
{
if (type == null)
return null;
return type.FullName;
}
set
{
if (value == null)
type = null;
else
{
type = Type.GetType(value);
}
}
}
// constructors
public SerializableType()
{
type = null;
}
public SerializableType(Type t)
{
type = t;
}
// allow SerializableType to implicitly be converted to and from System.Type
static public implicit operator Type(SerializableType stype)
{
return stype.type;
}
static public implicit operator SerializableType(Type t)
{
return new SerializableType(t);
}
// overload the == and != operators
public static bool operator ==(SerializableType a, SerializableType b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.type == b.type;
}
public static bool operator !=(SerializableType a, SerializableType b)
{
return !(a == b);
}
// we don't need to overload operators between SerializableType and System.Type because we already enabled them to implicitly convert
public override int GetHashCode()
{
return type.GetHashCode();
}
// overload the .Equals method
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to SerializableType return false.
SerializableType p = obj as SerializableType;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (type == p.type);
}
public bool Equals(SerializableType p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (type == p.type);
}
}
So I am trying to use it using but I have received the following error at runtime.
InvalidOperationException: System.RuntimeType is inaccessible due to
its protection level. Only public types can be processed.
Here is a fiddler with my Test Program. (Note you will have to copy it into an IDE as Fiddler doesn't have support for System.Runtime.Serialization right now)
For Completeness, my code is also below
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
public class Program
{
public static void Main()
{
var list = new List<ClassA>();
var classB = new ClassB();
var classC = new ClassC();
list.Add(new ClassA()
{
blah = "Test1",
InterfaceConcreteTypeA = classB.GetType(),
InterfaceConcreteTypeB = classC.GetType()
});
list.Add(new ClassA()
{
blah = "Test2",
InterfaceConcreteTypeA = classB.GetType(),
InterfaceConcreteTypeB = classC.GetType()
});
list.Add(new ClassA()
{
blah = "Test3",
InterfaceConcreteTypeA = classB.GetType(),
InterfaceConcreteTypeB = classC.GetType()
});
XmlSerializer ser = new XmlSerializer(typeof(ClassA[]));
using (XmlWriter writer = XmlWriter.Create("Test.xml"))
{
ser.Serialize(writer, list.ToArray());
}
}
[DataContract]
public class ClassA
{
[DataMember]
public string blah { get; set; }
[DataMember]
public SerializableType InterfaceConcreteTypeA { get; set; }
[DataMember]
public SerializableType InterfaceConcreteTypeB { get; set; }
}
public class ClassB { }
public class ClassC { }
// a version of System.Type that can be serialized
[DataContract]
public class SerializableType
{
public Type Type;
// when serializing, store as a string
[DataMember]
string TypeString
{
get
{
if (Type == null)
return null;
return Type.FullName;
}
set
{
if (value == null)
Type = null;
else
{
Type = Type.GetType(value);
}
}
}
// constructors
public SerializableType()
{
Type = null;
}
public SerializableType(Type t)
{
Type = t;
}
// allow SerializableType to implicitly be converted to and from System.Type
static public implicit operator Type(SerializableType stype)
{
return stype.Type;
}
static public implicit operator SerializableType(Type t)
{
return new SerializableType(t);
}
// overload the == and != operators
public static bool operator ==(SerializableType a, SerializableType b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.Type == b.Type;
}
public static bool operator !=(SerializableType a, SerializableType b)
{
return !(a == b);
}
// we don't need to overload operators between SerializableType and System.Type because we already enabled them to implicitly convert
public override int GetHashCode()
{
return Type.GetHashCode();
}
// overload the .Equals method
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to SerializableType return false.
SerializableType p = obj as SerializableType;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (Type == p.Type);
}
public bool Equals(SerializableType p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (Type == p.Type);
}
}
}
EDIT: I have resolved the first issue "System.RuntimeType is inaccessible due to its protection level." however now SerializableType is serialized as an empty tag. See my updated https://dotnetfiddle.net/ONPvDn
You have a field named 'type' that it's type is System.Type :/
That field just holding a reference to a RuntimeType instance which causes the error.
RuntimeType is not serializable...
Add an [XmlIgnore] attribute on it and you will be ok...
Update with result:

Generic ITypedList bound to a WinForms DataGridView: all rows show first item

I have a list of items which contain a dynamic property.
The goal is to have classes with defined properties, but also allow setting new properties to its instances on the fly. One sample is the 'MyDynamicObject' class in the code below.
The list (an instance of 'DynamicITypedList' in the code below) is then bound to a DataGridView, which will show and bind every property of the list items to a column.
The problem I face is: as soon as I make the 'DynamicITypedList' class implement 'ITypedList', all the rows in the DataGridView show the properties of the first item in the list !If I don't make the list class implement 'ITypedList' (comment out 'ITypedList' at the very end of the code below), the dynamically added properties values are not shown (because the specific GetItemProperties() is not called), but all rows show up in the DataGridView...
Can you please drive me towards a solution ?
Here is the calling code, ready to run in a WinForm containing one "datagridView1" control:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace Tests
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonITypedList_Click(object sender, EventArgs e)
{
MyDynamicObject myDynamicObject1 = new MyDynamicObject();
myDynamicObject1.Id = 1;
myDynamicObject1.AsDynamic.NewProperty = "NEWPROP1";
MyDynamicObject myDynamicObject2 = new MyDynamicObject();
myDynamicObject2.Id = 2;
DynamicITypedList<MyDynamicObject> myDynamicObjectDynamicITypedList = new DynamicITypedList<MyDynamicObject>();
myDynamicObjectDynamicITypedList.Add(myDynamicObject1);
myDynamicObjectDynamicITypedList.Add(myDynamicObject2);
dataGridView1.AutoGenerateColumns = false;
BindingSource bindingSource = new BindingSource();
bindingSource.DataSource = myDynamicObjectDynamicITypedList;
bindingSource.AllowNew = true;
dataGridView1.DataSource = bindingSource;
foreach (KeyValuePair<string, object> property in myDynamicObject1.Data.Properties)
{
DataGridViewTextBoxColumn dynamicPropertyColumn = new DataGridViewTextBoxColumn();
dynamicPropertyColumn.DataPropertyName = property.Key;
dynamicPropertyColumn.HeaderText = property.Key;
dynamicPropertyColumn.Name = property.Key;
dataGridView1.Columns.Add(dynamicPropertyColumn);
}
}
}
}
Below are the classes:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using System.Reflection;
namespace Tests
{
public interface IMyDynamic
{
MyDynamic Data { get; set; }
IEnumerable<string> GetDynamicMemberNames();
}
public class MyDynamic : DynamicObject, IDynamicMetaObjectProvider
{
object Instance { get; set; }
Type InstanceType { get; set; }
Dictionary<string, MemberInfo> MemberInfos { get; set; }
PropertyInfo[] instancePropertyInfo;
PropertyInfo[] InstancePropertyInfo
{
get
{
if (instancePropertyInfo == null && Instance != null)
instancePropertyInfo = Instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
return instancePropertyInfo;
}
}
public MyDynamic()
{
Initialize(this);
}
public MyDynamic(object instance)
{
Initialize(instance);
}
public IEnumerable<KeyValuePair<string, object>> GetProperties(bool includeInstanceProperties = false)
{
if (includeInstanceProperties && Instance != null)
{
foreach (var prop in this.InstancePropertyInfo)
yield return new KeyValuePair<string, object>(prop.Name, prop.GetValue(Instance, null));
}
foreach (var key in this.Properties.Keys)
yield return new KeyValuePair<string, object>(key, this.Properties[key]);
}
public override IEnumerable<string> GetDynamicMemberNames()
{
foreach (var prop in GetProperties(true))
yield return prop.Key;
}
public Dictionary<string, object> Properties { get; private set; }
public object this[string key]
{
get
{
try
{
//Try to get from properties collection first
return Properties[key];
}
catch (KeyNotFoundException)
{
//Try reflection on instanceType
object result = null;
if (GetProperty(Instance, key, out result))
return result;
//Nope doesn't exist
//throw; //Preserve the stack trace
return null;
}
}
set
{
if (Properties.ContainsKey(key))
{
Properties[key] = value;
return;
}
//Check instance for existance of type first
var miArray = InstanceType.GetMember(key, BindingFlags.Public | BindingFlags.GetProperty);
if (miArray != null && miArray.Length > 0)
SetProperty(Instance, key, value);
else
Properties[key] = value;
}
}
protected virtual void Initialize(object instance)
{
Instance = instance;
if (instance != null)
InstanceType = instance.GetType();
Properties = new Dictionary<string, object>();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
//First check the Properties collection for member
if (Properties.Keys.Contains(binder.Name))
{
result = Properties[binder.Name];
return true;
}
//Next check for Public properties via Reflection
if (Instance != null)
{
try
{
return GetProperty(Instance, binder.Name, out result);
}
catch { }
}
//Failed to retrieve a property
result = null;
return false;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
//First check to see if there's a native property to set
if (Instance != null)
{
try
{
if (SetProperty(Instance, binder.Name, value))
return true;
}
catch { }
}
//No match - set or add to dictionary
Properties[binder.Name] = value;
return true;
}
protected bool GetProperty(object instance, string name, out object result)
{
if (instance == null)
instance = this;
if (MemberInfos == null)
{
MemberInfos = new Dictionary<string, MemberInfo>();
MemberInfo[] memberInfos = InstanceType.GetMember(name, BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance);
foreach (MemberInfo instanceMemberInfo in InstanceType.GetMembers())
MemberInfos.Add(instanceMemberInfo.Name, instanceMemberInfo);
}
result = null;
MemberInfo propertyMemberInfo;
if (!MemberInfos.TryGetValue(name, out propertyMemberInfo))
return false;
if (propertyMemberInfo.MemberType == MemberTypes.Property)
{
result = ((PropertyInfo)propertyMemberInfo).GetValue(instance, null);
return true;
}
return false;
}
protected bool SetProperty(object instance, string name, object value)
{
if (instance == null)
instance = this;
if (MemberInfos == null)
{
MemberInfos = new Dictionary<string, MemberInfo>();
MemberInfo[] memberInfos = InstanceType.GetMember(name, BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance);
foreach (MemberInfo instanceMemberInfo in InstanceType.GetMembers())
MemberInfos.Add(instanceMemberInfo.Name, instanceMemberInfo);
}
MemberInfo propertyMemberInfo;
if (!MemberInfos.TryGetValue(name, out propertyMemberInfo))
return false;
if (propertyMemberInfo.MemberType == MemberTypes.Property)
{
((PropertyInfo)propertyMemberInfo).SetValue(Instance, value, null);
return true;
}
return false;
}
}
public /*abstract*/ class MyDynamicObject : MyDynamic, IMyDynamic
{
public MyDynamic Data
{
get { return data; }
set { data = dynamicData = value; }
}
public dynamic AsDynamic
{
get { return dynamicData; }
}
public Int64 Id { get { return AsDynamic.Id; } set { AsDynamic.Id = value; } }
MyDynamic data;
dynamic dynamicData;
public MyDynamicObject() : this(new MyDynamic())
{ }
public MyDynamicObject(MyDynamic data)
{
Data = data;
TypeDescriptor.AddProvider(new MyDynamicTypeDescriptionProvider(), this); //Data);
}
}
public class IMyDynamicTypeDescriptor : ICustomTypeDescriptor
{
private readonly IMyDynamic m_Instance;
public IMyDynamicTypeDescriptor(object instance)
{
if (instance is MyDynamicObject)
m_Instance = (MyDynamicObject)instance;
else if (instance is IMyDynamic)
m_Instance = (IMyDynamic)instance;
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return m_Instance;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return null;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return new PropertyDescriptorCollection(
m_Instance.Data.Properties.Keys
.Select(x => new MyDynamicPropertyDescriptor(m_Instance, x))
.ToArray<PropertyDescriptor>());
}
}
public class MyDynamicPropertyDescriptor : PropertyDescriptor
{
private readonly IMyDynamic m_Instance;
private readonly string m_Name;
public MyDynamicPropertyDescriptor(IMyDynamic instance, string name)
: base(name, null)
{
m_Instance = instance;
m_Name = name;
}
public override Type PropertyType
{
get
{
if (m_Instance.Data[m_Name] != null)
return m_Instance.Data[m_Name].GetType();
else
{
//...
return typeof(string);
}
}
}
public override void SetValue(object component, object value)
{
m_Instance.Data[m_Name] = value;
}
public override object GetValue(object component)
{
return m_Instance.Data[m_Name];
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type ComponentType
{
get { return null; }
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{ }
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override string Category
{
get { return string.Empty; }
}
public override string Description
{
get { return string.Empty; }
}
}
public class MyDynamicTypeDescriptionProvider : TypeDescriptionProvider
{
private static readonly TypeDescriptionProvider m_Default = TypeDescriptor.GetProvider(typeof(ExpandoObject));
public MyDynamicTypeDescriptionProvider() : base(m_Default)
{ }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
var defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
return (instance == null ? defaultDescriptor : new IMyDynamicTypeDescriptor(instance));
}
}
public class DynamicITypedList<T> : ObservableCollection<T>, /*List<T>, IList<T>,*/ ITypedList where T : IMyDynamic, new()
{
public string GetListName(PropertyDescriptor[] listAccessors)
{
return null;
}
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
return TypeDescriptor.GetProperties(this.FirstOrDefault());
}
}
}
this is not really an answer to the question directly, but...
I had the same task some days ago. Bind dynamic objects to a datagrid.
My solution is:
Add the columns to the datagrid manually
Set the databinding-properties manually
Use BindingSource, bind this BindingSource to your List then bind your grid to the bindingsource
Be sure to do all of this in the correct order, because if you don't, your dynamic properties are unknown to the grid/bindingsource and therefore won't be shown.
I don't remember the exact order of all of this, maybe you should try around a little with my hints and come back and ask further?

Comparing (specific) object properties in c#

Building upon this answer for comparing objects in C#
Comparing object properties in c#
Knowing this is a complex topic, I wanted to handle a few more specific structures.
While this code will match properties if they are simple value types such as this object:
public class BasicStuff
{
public int anInt { get; set; }
public bool aBool { get; set; }
}
But as soon as it gets any more complex, this code fails.
So what I would like to do is make it a bit more usable for nested objects of the above, such as:
public class NestedStuff
{
public BasicStuff theBasic { get; set; }
}
Any array of the above, such as:
public class ArrayStuff
{
public BasicStuff[] theBasicArray { get; set; }
}
And finally any combination of the above:
public class AllTheStuff
{
public int anInt { get; set; }
public bool aBool { get; set; }
public BasicStuff theBasic { get; set; }
public BasicStuff[] theBasicArray { get; set; }
}
So what I came up with was the following:
public static bool AllPublicPropertiesEqual<T>(T AObj, T BObj, params string[] ignore) where T : class
{
if (AObj != null && BObj != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(ignore);
foreach (PropertyInfo pInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!ignoreList.Contains(pInfo.Name))
{
if (pInfo.PropertyType.IsArray)
{
object AValue = type.GetProperty(pInfo.Name).GetValue(AObj, null);
object BValue = type.GetProperty(pInfo.Name).GetValue(BObj, null);
string t = AValue.GetType().ToString();
if (!AllPublicPropertiesEqual<object>(AValue, BValue))
return false;
}
else if (!pInfo.PropertyType.IsValueType && !pInfo.PropertyType.IsPrimitive && !pInfo.PropertyType.IsEnum && pInfo.PropertyType != typeof(string))
//else if (Convert.GetTypeCode(pInfo.PropertyType) == TypeCode.Object)
{
object AValue = type.GetProperty(pInfo.Name).GetValue(AObj, null);
object BValue = type.GetProperty(pInfo.Name).GetValue(BObj, null);
if (!AllPublicPropertiesEqual<object>(BValue, AValue))
return false;
}
else
{
object selfValue = type.GetProperty(pInfo.Name).GetValue(AObj, null);
object toValue = type.GetProperty(pInfo.Name).GetValue(BObj, null);
if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
return false;
}
}
}
return true;
}
return AObj == BObj;
}
Only this fails because when recursively calling AllPublicPropertiesEqual, I need to pass the specific values type rather than just a generic object type.
But I dont know how to do this.... Any ideas are greatly appreciated...
Your method does not have to be generic. You can change the beginning of the method to:
public static bool AllPublicPropertiesEqual(object AObj, object BObj, params string[] ignore)
{
if (AObj != null && BObj != null)
{
Type type = AObj.GetType();
if (BObj.GetType() != type)
throw new Exception("Objects should be of the same type");
....
}
....
}
Well first thing what is that you should create Interface for those classes because from what I see you can combine those classes but all of them will have the same properties. Second option is create some Base abstract class with those properties and inherit from it. It is up to you what you choose.
Than create in this Base class or Interface Equals functions where you will equal directly the shared properties it is much easier and I think more efficient :-)
public abstract class BaseStaff {
public int anInt { get; set; }
public bool aBool { get; set; }
public abstract bool Equals(BaseStaff anotherStaff);
}
Then you can use this class and create your BasicStaff class and AllTheStaff class too in BasicStaff implement Equals method like this.
public override bool Equals(BaseStaff staff) {
this.anInt == staff.anInt &&
this.aBool == staff.aBool;
}
In AllTheStaff u can than override this method like this
public override bool Equals(BaseStaff staff) {
bool baseEquals = base.Equals(staff);
bool basicStaffEquals = this.BasicStaff.Equals(staff);
return baseEquals || basicStaffEquals;
}
This is just core idea and maybe I dont understand you well what you really want to achieve but hope it helps you :)
tested Solution
public static bool AllPublicPropertiesEqual<T>(T AObj, T BObj, params string[] ignore) where T : class
{
if (AObj != null && BObj != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(ignore);
foreach (PropertyInfo pInfo in type.GetCType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!ignoreList.Contains(pInfo.Name))
{
if (pInfo.PropertyType.IsArray)
{
object aElementValues = (type.GetProperty(pInfo.Name).GetValue(AObj, null));
object bElementValues = (type.GetProperty(pInfo.Name).GetValue(BObj, null));
if (aElementValues != null && bElementValues != null)
{
List<object> AListValues = new List<object>();
List<object> BListValues = new List<object>();
foreach (var v in (IEnumerable)aElementValues)
AListValues.Add(v);
foreach (var v in (IEnumerable)bElementValues)
BListValues.Add(v);
if (AListValues.Count == BListValues.Count)
{
object[] aArray = AListValues.ToArray();
object[] bArray = BListValues.ToArray();
for (int i = 0; i < aArray.Length; i++)
{
if (!AllPublicPropertiesEqual(aArray[i], bArray[i]))
return false;
}
}
else
return false;
}
else if ((aElementValues == null) != (bElementValues == null))
return false;
}
else if (!pInfo.PropertyType.IsValueType && !pInfo.PropertyType.IsPrimitive && !pInfo.PropertyType.IsEnum && pInfo.PropertyType != typeof(string))
//else if (Convert.GetTypeCode(pInfo.PropertyType) == TypeCode.Object)
{
object AObjectValue = type.GetProperty(pInfo.Name).GetValue(AObj, null);
object BObjectValue = type.GetProperty(pInfo.Name).GetValue(BObj, null);
if (!AllPublicPropertiesEqual(BObjectValue, AObjectValue))
return false;
}
else
{
object aValue = type.GetProperty(pInfo.Name).GetValue(AObj, null);
object bValue = type.GetProperty(pInfo.Name).GetValue(BObj, null);
if (aValue != bValue && (aValue == null || !aValue.Equals(bValue)))
return false;
}
}
}
return true;
}
return AObj == BObj;
}
}

Adding a TypeConverter to dynamic properties

I am coding a C# forms application where an object has custom properties for a PropertyGrid. These custom properties are displayed at runtime.
Here is my code:
private readonly List<CustomProperty> props = new List<CustomProperty>();
[Browsable(false)]
public List<CustomProperty> properties { get { return props; } }
private Dictionary<string, object> values = new Dictionary<string, object>();
public object this[string name]
{
get { object val; values.TryGetValue(name, out val); return val; }
set
{
values[name] = value;
}
}
private class CustomWebpageObjectTypeUserObjectConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
var stdProps = base.GetProperties(context, value, attributes);
CustomWebpageObjectTypeUserObject obj = value as CustomWebpageObjectTypeUserObject;
List<CustomProperty> customProps = obj == null ? null : obj.properties;
PropertyDescriptor[] props = new PropertyDescriptor[stdProps.Count + (customProps == null ? 0 : customProps.Count)];
stdProps.CopyTo(props, 0);
if (customProps != null)
{
int index = stdProps.Count;
foreach (CustomProperty prop in customProps)
{
CustomWebpageObjectTypeUserObjectCustomPropertyDescriptor customWebpageObjectTypeUserObjectCustomPropertyDescriptor = new CustomWebpageObjectTypeUserObjectCustomPropertyDescriptor(prop);
props[index++] = customWebpageObjectTypeUserObjectCustomPropertyDescriptor;
}
}
return new PropertyDescriptorCollection(props);
}
}
public class CustomWebpageObjectTypeUserObjectCustomPropertyDescriptor : PropertyDescriptor
{
private readonly CustomProperty prop;
public CustomWebpageObjectTypeUserObjectCustomPropertyDescriptor(CustomProperty prop)
: base(prop.name, null)
{
this.prop = prop;
}
public override string Category { get { return prop.category; } }
public override string Description { get { return prop.description; } }
public override string Name { get { return prop.name; } }
public override bool ShouldSerializeValue(object component) { return ((CustomWebpageObjectTypeUserObject)component)[prop.name] != null; }
public override void ResetValue(object component) { ((CustomWebpageObjectTypeUserObject)component)[prop.name] = null; }
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType { get { return prop.type; } }
public override bool CanResetValue(object component) { return true; }
public override Type ComponentType { get { return typeof(CustomWebpageObjectTypeUserObject); } }
public override void SetValue(object component, object value)
{
((CustomWebpageObjectTypeUserObject)component)[prop.name] = value;
prop._value = value;
}
public override object GetValue(object component)
{
return ((CustomWebpageObjectTypeUserObject)component)[prop.name] ?? prop._value;
}
}
This code works correctly with simple set and get values. What I am needing some advice with is how to display a drop down list with dynamic properties that are created at runtime.
I understand how to use a TypeConverter to get a StandardValuesCollection, yet am not sure how to do this with dynamic properties. Do I need to add this attribute to a dynamic property, and does this need to be done in the CustomWebpageObjectTypeUserObjectCustomPropertyDescriptor class? Is there an override that I need to implement to add this attribute? Do I need to do this another way?
Thanks in advance.

Categories

Resources