IEnumerable<T> and reflection - c#

Background
Working in .NET 2.0 Here, reflecting lists in general. I was originally using t.IsAssignableFrom(typeof(IEnumerable)) to detect if a Property I was traversing supported the IEnumerable Interface. (And thus I could cast the object to it safely)
However this code was not evaluating to True when the object is a BindingList<T>.
Next
I tried to use t.IsSubclassOf(typeof(IEnumerable)) and didn't have any luck either.
Code
/// <summary>
/// Reflects an enumerable (not a list, bad name should be fixed later maybe?)
/// </summary>
/// <param name="o">The Object the property resides on.</param>
/// <param name="p">The Property We're reflecting on</param>
/// <param name="rla">The Attribute tagged to this property</param>
public void ReflectList(object o, PropertyInfo p, ReflectedListAttribute rla)
{
Type t = p.PropertyType;
//if (t.IsAssignableFrom(typeof(IEnumerable)))
if (t.IsSubclassOf(typeof(IEnumerable)))
{
IEnumerable e = p.GetValue(o, null) as IEnumerable;
int count = 0;
if (e != null)
{
foreach (object lo in e)
{
if (count >= rla.MaxRows)
break;
ReflectObject(lo, count);
count++;
}
}
}
}
The Intent
I want to basically tag lists i want to reflect through with the ReflectedListAttribute and call this function on the properties that has it. (Already Working)
Once inside this function, given the object the property resides on, and the PropertyInfo related, get the value of the property, cast it to an IEnumerable (assuming it's possible) and then iterate through each child and call ReflectObject(...) on the child with the count variable.

When you do the as IEnumerable and the variable is not null you know that it does implement IEnumerable interface.
You donĀ“t need the code:
Type t = p.PropertyType;
//if (t.IsAssignableFrom(typeof(IEnumerable)))
if (t.IsSubclassOf(typeof(IEnumerable)))
{
This would be enough:
public void ReflectList(object o, PropertyInfo p, ReflectedListAttribute rla)
{
IEnumerable e = p.GetValue(o, null) as IEnumerable;
int count = 0;
if (e != null)
{
foreach (object lo in e)
{
if (count >= rla.MaxRows)
break;
ReflectObject(lo, count);
count++;
}
}
}

From MSDN
The IsSubclassOf method cannot be used
to determine whether an interface
derives from another interface, or
whether a class implements an
interface Use the GetInterface method for that purpose
Also your implementation of IsAssignableFrom is wrong, you should use it like this:
typeof(IEnumerable).IsAssignableFrom(t)
This should return true if IEnumerable is implemented by t..

Why do you the if-statement at all?
You already did a var e = ... as IEnumerable and afterwards just check if is not null.
Isn't that enough?

These work. :)
A List is extended Collection. So, the tests are different for them. A Dictionary has got two internal containers. Hence one test for the same.
public static bool IsList(object obj)
{
System.Collections.IList list = obj as System.Collections.IList;
return list != null;
}
public static bool IsCollection(object obj)
{
System.Collections.ICollection coll = obj as System.Collections.ICollection;
return coll != null;
}
public static bool IsDictionary(object obj)
{
System.Collections.IDictionary dictionary = obj as System.Collections.IDictionary;
return dictionary != null;
}
Usage example -
if (IsDictionary(fieldObject)) //key-value type collection?
{
System.Collections.IDictionary dictionary = fieldObject as System.Collections.IDictionary;
foreach (object _ob in dictionary.Values)
{
//do work
}
// dictionary.Clear();
}
else //normal collection
{
if (IsCollection(fieldObject))
{
System.Collections.ICollection coll = fieldObject as System.Collections.ICollection;
foreach (object _ob in coll)
{
//do work
}
if (IsList(fieldObject))
{
//System.Collections.IList list = fieldObject as System.Collections.IList;
//list.Clear(); // <--- List's function, not Collection's.
}
}
}

Related

Compare generic two objects

I'm trying to do a simple comparison on two objects, simply to track changes on saving to the database, I have the below code which works perfectly on simple objects.
public static List<Variance> Compare<T>(this T original, T current)
{
var variances = new List<Variance>();
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var v = new Variance
{
PropertyName = property.Name,
Original = property.GetValue(original),
Current = property.GetValue(current)
};
if (v.Original == null && v.Current == null)
{
continue;
}
if ((v.Original == null && v.Current != null) || (v.Original != null && v.Current == null))
{
variances.Add(v);
continue;
}
if (!v.Original.Equals(v.Current))
{
variances.Add(v);
}
}
return variances;
}
but if T is a list the equals need to be changed to a SequenceEqual and I can't think of a way to convert T to the correct list type to do the SequenceEqual check.
Although it's not impossible to use SequenceEqual here, it'd involve either dynamic like in Jasper Kent's answer, or quite a lot of reflection code, I'll outline what you'll need to do if you were using reflection:
check if both the objects are IEnumerable<T>.
get the Type of both objects, and then get the type parameter T as a Type as well
get the method SequenceEquals from Enumerable as a MethodInfo. This involves using LINQ to find the overload with two parameters.
call MakeGenericMethod with T.
Invoke the MethodInfo
I wouldn't want to read, or write that code... Using dynamic is fine I guess, though some people have different opinions...
So here's a third way: I suggest you write your own SequenceEqual method that takes IEnumerables (the non-generic version):
private static bool SequenceEqual(IEnumerable first, IEnumerable second) {
IEnumerator e1 = first.GetEnumerator();
IEnumerator e2 = second.GetEnumerator();
try {
// adapted from https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,63644a881e976b52,references
while (e1.MoveNext())
{
if (!(e2.MoveNext() && e1.Current.Equals(e2.Current))) return false;
}
if (e2.MoveNext()) return false;
return true;
} finally {
if (e1 is IDisposable d1) {
d1.Dispose();
}
if (e2 is IDisposable d2) {
d2.Dispose();
}
}
}
Then you can just check whether the objects are IEnumerable:
if (v.Original is Enumerable e1 &&
v.Current is Enumerable e2 &&
!SequenceEqual(e1, e2))
{
variances.Add(v);
}
I think the solution you want is to use dynamic overloading.
I've cut this solution down from yours to get to the nub of it. The call to SwitchEquals() is the equivalent of your v.Original.Equals(v.Current).
static bool SwitchEquals<T>(IEnumerable<T> listA, IEnumerable<T> listB)
{
Console.WriteLine("Doing Sequential Equals");
return true; // Do your sequential equal here
}
static bool SwitchEquals(object objA, object objB)
{
Console.WriteLine("Doing equals");
return objA.Equals(objB); // This is your original equals
}
static void Compare<T>(T original, T current)
{
// Using dynamic means the decision between the tow SwitchEquals methods is made
// At runtime, when the system knows if we have a collection
if (SwitchEquals ((dynamic)original,(dynamic) current))
Console.WriteLine("Match");
else
Console.WriteLine("No match");
}
static void Main(string[] args)
{
Compare(4,5);
Compare (new int[] { 4, 3 }, new int[] { 4, 4 });
}

.NET reflection - check 2 classes are added as members of each other

How can I check 2 classes are added as members of each other.
class Team
{
Driver driver{ get; set;}
Driver codriver{ get; set;}
}
class Driver
{
Team parentTeam{ get; set;}
}
I have used the following way to get the properties:-
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
////if (!isParent) //need to find this.
//{
object propertyValue = property.GetValue(obj);
if (propertyValue != null)
{
// Get the deep clone of the field in the original object and assign the clone to the field in the new object.
property.SetValue(copiedObject, CloneProcedure(propertyValue));
}
//}
}
I want to skip through the first class which is a property in second when iterating through 2nd one.
Note: Some of you may feel my classes are declared incorrectly but this is a legacy system and there is no chance of me restructuring the classes.
I have tried DeclaringType and I get property.DeclaringType but obj.GetType().DeclaringType is null.
By the looks of it you are deep cloning, and you don't actually want to skip a parent Type, you just don't want the same instance to generate multiple clones.
What you could do is keep a Dictionary<object, object> that keeps references of previously cloned instances:
object CloneProcedure(object o, Dictionary<object, object> cloned)
{
object clone;
if (cloned.TryGetValue(o, out clone))
{
// this object has been cloned earlier, return reference to that clone
return clone;
}
clone = Activator.CreateInstance(o.GetType());
cloned[o] = clone;
PropertyInfo[] properties = ...
foreach ...
{
...
property.SetValue(copiedObject, CloneProcedure(propertyValue, cloned));
}
}
This ensures that no object is ever cloned multiple times, and if multiple properties point to the same instance, the clones will also point to the same cloned instance.
Try this
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(Parent))
{
object propertyValue = property.GetValue(obj);
if (propertyValue != null)
{
// Get the deep clone of the field in the original object and assign the clone to the field in the new object.
property.SetValue(copiedObject, CloneProcedure(propertyValue));
}
}
}
If you want to allow for inheritance, you can use Type.IsAssignableFrom
I am posting my final Deep copy utility if this will benefit anyone
public static class DeepCloneHelper
{
private static string[] _excludedPropertyNames = null;
/// <summary>
/// Get the deep clone of an object.
/// </summary>
/// <typeparam name="T">The type of the source.</typeparam>
/// <param name="source">It is the object used to deep clone.</param>
/// <param name="propertyNames"></param>
/// <returns>Return the deep clone.</returns>
public static T DeepClone<T>(T source, string[] propertyNames = null)
{
if (source == null)
{
throw new ArgumentNullException("Object is null");
}
if (propertyNames != null) { _excludedPropertyNames = propertyNames; }
return (T)CloneProcedure(source, new Dictionary<object, object>());
// return target;
}
/// <summary>
/// The method implements deep clone using reflection.
/// </summary>
/// <param name="source">It is the object used to deep clone.</param>
/// <param name="cloned"></param>
/// <returns>Return the deep clone.</returns>
private static object CloneProcedure(Object source, Dictionary<object, object> cloned)
{
if (source == null)
{
return null;
}
object clone;
if (cloned.TryGetValue(source, out clone))
{
// this object has been cloned earlier, return reference to that clone
return clone;
}
Type type = source.GetType();
// If the type of object is the value type, we will always get a new object when
// the original object is assigned to another variable. So if the type of the
// object is primitive or enum, we just return the object. We will process the
// struct type subsequently because the struct type may contain the reference
// fields.
// If the string variables contain the same chars, they always refer to the same
// string in the heap. So if the type of the object is string, we also return the
// object.
if (type.IsPrimitive || type.IsEnum || type == typeof(string))
{
return source;
}
// If the type of the object is the Array, we use the CreateInstance method to get
// a new instance of the array. We also process recursively this method in the
// elements of the original array because the type of the element may be the reference
// type.
else if (type.IsArray)
{
Type typeElement = Type.GetType(type.FullName.Replace("[]", string.Empty) + "," + type.Assembly.FullName);
var array = source as Array;
Array copiedArray = Array.CreateInstance(typeElement, array.Length);
cloned[source] = copiedArray;
for (int i = 0; i < array.Length; i++)
{
// Get the deep clone of the element in the original array and assign the
// clone to the new array.
copiedArray.SetValue(CloneProcedure(array.GetValue(i), cloned), i);
}
return copiedArray;
}
else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
if (typeof(IList).IsAssignableFrom(type))
{
var collection = (IList)Activator.CreateInstance(type);
cloned[source] = collection;
foreach (var element in source as IEnumerable)
{
collection.Add(CloneProcedure(element, cloned));
}
return collection;
}
else if (type.IsGenericType)
{
var objectType = type.GetGenericArguments().Single();
if (typeof(IList<>).MakeGenericType(objectType).IsAssignableFrom(type) ||
typeof(ISet<>).MakeGenericType(objectType).IsAssignableFrom(type))
{
var collection = Activator.CreateInstance(type);
cloned[source] = collection;
var addMethod = collection.GetType().GetMethod("Add");
foreach (var element in source as IEnumerable)
{
addMethod.Invoke(collection, new[] { CloneProcedure(element, cloned) });
}
return collection;
}
}
return source;
}
// If the type of the object is class or struct, it may contain the reference fields,
// so we use reflection and process recursively this method in the fields of the object
// to get the deep clone of the object.
// We use Type.IsValueType method here because there is no way to indicate directly whether
// the Type is a struct type.
else if (type.IsClass || type.IsValueType)
{
object copiedObject = Activator.CreateInstance(source.GetType());
cloned[source] = copiedObject;
// Get all PropertyInfo.
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
if (_excludedPropertyNames == null || !_excludedPropertyNames.Contains(property.Name))
{
object propertyValue = property.GetValue(source);
if (propertyValue != null && property.CanWrite && property.GetSetMethod() != null)
{
// Get the deep clone of the field in the original object and assign the
// clone to the field in the new object.
property.SetValue(copiedObject, CloneProcedure(propertyValue, cloned));
}
}
}
return copiedObject;
}
else
{
throw new ArgumentException("The object is unknown type");
}
}
}
Reference : https://code.msdn.microsoft.com/windowsdesktop/CSDeepCloneObject-8a53311e

Mapping List of sourceObject to List of DestinationObject

I am using the code from Apply properties values from one object to another of the same type automatically?
public static class Reflection
{
/// <summary>
/// Extension for 'Object' that copies the properties to a destination object.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="destination">The destination.</param>
public static void CopyProperties(this object source, object destination)
{
// If any this null throw an exception
if (source == null || destination == null)
throw new Exception("Source or/and Destination Objects are null");
// Getting the Types of the objects
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
// Iterate the Properties of the source instance and
// populate them from their desination counterparts
PropertyInfo[] srcProps = typeSrc.GetProperties();
foreach (PropertyInfo srcProp in srcProps)
{
if (!srcProp.CanRead)
{
continue;
}
PropertyInfo targetProperty = typeDest.GetProperty(srcProp.Name);
if (targetProperty == null)
{
continue;
}
if (!targetProperty.CanWrite)
{
continue;
}
if (targetProperty.GetSetMethod(true) != null && targetProperty.GetSetMethod(true).IsPrivate)
{
continue;
}
if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
{
continue;
}
if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType))
{
continue;
}
// Passed all tests, lets set the value
targetProperty.SetValue(destination, srcProp.GetValue(source, null), null);
}
}
}
That all works great!
What am having trouble trying to figure out is how to create a similar function that takes a List of source and copy to a List of destination and use that to call the code above.
Of course this doesn't work but Something like:
public static void CopyListProperties(this List<object> sourceList, List<object> destinationList)
{
foreach (var item in sourceList)
{
var destinationObject = new destinationObjectType();
item.CopyProperties(destinationObject);
destinationList.Add(destinationObject);
}
}
Thanks to Alex on this one.
Here is the function how it should be.
public static void CopyListProperties<T>(this List<object> sourceList, List<T> destinationList) where T: new()
{
foreach (var item in sourceList)
{
var destinationObject = new T();
item.CopyProperties(destinationObject);
destinationList.Add(destinationObject);
}
}

Checking if a generic IEnumerable is empty

Let's say I have an object which may be of type IEnumerable<T>. I want to write a method that returns true if the object is of type IEnumerable<T>, is not null, and is not empty.
Here's what I've got so far:
public bool IsNullOrEmpty(object obj)
{
if (obj != null)
{
if (obj is IEnumerable<object>)
{
return (obj as IEnumerable<object>).Any();
}
}
return false;
}
This works if I pass in an object that is of type List<string>, but not if I pass in an object that is of type List<int>. It fails because because obj is IEnumerable<object> returns false.
Any idea how I can make this work for all generic IEnumerables?
Since the type may be unknown, you can try check for IEnumerable interface and use MoveNext() on the enumerator.
EDIT: I updated the method name. It makes more sense with the logic now since the original question code was checking if there were items in the collection.
public bool IsNotNullOrEmpty(object enumerable)
{
if (enumerable != null)
{
if (enumerable is IEnumerable)
{
using(var enumerator = ((IEnumerable)enumerable).GetEnumerator())
return enumerator.MoveNext();
}
}
return false;
}
System.Collections.Generic.IEnumerable<T> inherits from System.Collections.IEnumerable - thus, if you are ok with checking the non-generic IEnumerable, rather than the generic IEnumerable<T>, you could just cast to IEnumerable.
A few notes about your code: You are first checking with is, and then you cast with as. That is generally unnecessary; as already checks and returns null if the cast failed. Therefore, a shorter way would be:
var enumerable = obj as IEnumerable;
if (enumerable != null) {
return !enumerable.Cast<object>().Any();
}
Note that you will need the additional call to Cast there, as Any requires a generic IEnumerable<T>.
You can try to cast it to IEnumerable:
public static bool IsNullOrEmpty<T>(this T obj) where T : class
{
if (obj == null) return true;
IEnumerable seq = obj as IEnumerable;
if (seq != null) return !seq.Cast<object>().Any();
return false;
}
...
List<int> list = new List<int>();
bool nullOrEmpty = list.IsNullOrEmpty(); // true
Btw, interestingly enough it works correctly with an empty string:
bool nullOrEmpty = "".IsNullOrEmpty(); // true
You can check for the non-generic IEnumerable and check that for emptiness. You can add a check to ensure the object implements IEnumerable<T> using reflection:
public static bool IsNullOrEmpty(object obj)
{
var e = obj as System.Collections.IEnumerable;
if (e == null || !e.GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))) return false;
foreach (object _ in e)
{
return true;
}
return false;
}
For completeness, adding the state-of-the-art answers for the recent c# versions (from c#8.0):
static bool IsNotNullOrEmpty<T>(object obj) => obj is IEnumerable<T> seq && seq.Any();
static bool IsNullOrEmpty<T>(object obj) => !(obj is IEnumerable<T> seq && seq.Any());
This assumes you will know T in the calling site.
If you won't know T, you shouldn't be using IEnumerable<object>, but IEnumerable instead. Something like this:
static bool IsNotNullOrEmpty(object obj) => obj is IEnumerable seq && seq.GetEnumerator().MoveNext();
You can first check if the object implements ICollection like lists and arrays do, as checking the size of one of those is cheaper as they have a Count property. If it's not you can check if it implements IEnumerable and if it does, create an enumerator and see if you can move to the first item:
public static bool IsNullOrEmpty(object obj) {
ICollection c = obj as ICollection;
if (c != null) {
return c.Count == 0;
}
IEnumerable e = obj as IEnumerable;
if (e != null) {
return !e.GetEnumerator().MoveNext();
}
return false;
}
This answers expands the question outside of the IEnumerable<T> scope to "can this thing I have be used in a foreach-loop?".
As you might known, foreach does not check for IEnumerable primarily, but rather tries to enumerate whatever is given.
This will work for all types which in some sense is enumerable in the sense that foreach thinks:
public bool IsNullOrEmpty(object obj)
{
if (obj != null) // we can't be null
{
var method = obj.GetType().GetMethod("GetEnumerator");
if (method != null) // we have an enumerator method
{
var enumerator = method.Invoke(obj, null);
if (enumerator != null) // we got the enumerator
{
var moveMethod = enumerator.GetType().GetMethod("MoveNext")
if (moveMethod != null) // we have a movenext method, lets try it
return !(bool)moveMethod.Invoke(enumerator, null); // ie true = empty, false = has elements
}
}
}
return true; // it's empty, lets return true
}
This basically does the same as the foreach-loop, ie checks for enumerability and doesn't really give a darn about the types involved.
As such it should really be avoided, but if you somehow have to check if you can put your type in foreach-loop this should do the trick.

SortList duplicated key, but it shouldn't

I have a class which implements IList interface. I requires a "sorted view" of this list, but without modifying it (I cannot sort directly the IList class).
These view shall be updated when the original list is modified, keeping items sorted. So, I've introduced a SortList creation method which create a SortList which has a comparer for the specific object contained in the original list.
Here is the snippet of code:
public class MyList<T> : ICollection, IList<T>
{
public SortedList CreateSortView(string property)
{
try
{
Lock();
SortListView sortView;
if (mSortListViews.ContainsKey(property) == false)
{
// Create sorted view
sortView = new SortListView(property, Count);
mSortListViews.Add(property, sortView);
foreach (T item in Items)
sortView.Add(item);
} else
sortView = mSortListViews[property];
sortView.ReferenceCount++;
return (sortView);
}
finally
{
Unlock();
}
}
public void DeleteSortView(string property)
{
try
{
Lock();
// Unreference sorted view
mSortListViews[property].ReferenceCount--;
// Remove sorted view
if (mSortListViews[property].ReferenceCount == 0)
mSortListViews.Remove(property);
}
finally
{
Unlock();
}
}
protected class SortListView : SortedList
{
public SortListView(string property, int capacity)
: base(new GenericPropertyComparer(typeof(T).GetProperty(property, BindingFlags.Instance | BindingFlags.Public)), capacity)
{
}
public int ReferenceCount = 0;
public void Add(T item)
{
Add(item, item);
}
public void Remove(T item)
{
base.Remove(item);
}
class GenericPropertyComparer : IComparer
{
public GenericPropertyComparer(PropertyInfo property)
{
if (property == null)
throw new ArgumentException("property doesn't specify a valid property");
if (property.CanRead == false)
throw new ArgumentException("property specify a write-only property");
if (property.PropertyType.GetInterface("IComparable") == null)
throw new ArgumentException("property type doesn't IComparable");
mSortingProperty = property;
}
public int Compare(object x, object y)
{
IComparable propX = (IComparable)mSortingProperty.GetValue(x, null);
IComparable propY = (IComparable)mSortingProperty.GetValue(y, null);
return (propX.CompareTo(propY));
}
private PropertyInfo mSortingProperty = null;
}
private Dictionary<string, SortListView> mSortListViews = new Dictionary<string, SortListView>();
}
Practically, class users request to create a SortListView specifying the name of property which determine the sorting, and using the reflection each SortListView defined a IComparer which keep sorted the items.
Whenever an item is added or removed from the original list, every created SortListView will be updated with the same operation.
This seems good at first chance, but it creates me problems since it give me the following exception when adding items to the SortList:
System.ArgumentException: Item has already been added. Key in dictionary: 'PowerShell_ISE [C:\Windows\sysWOW64\WindowsPowerShell\v1.0\PowerShell_ISE.exe]' Key being added: 'PowerShell_ISE [C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe]'
As you can see from the exception message, thrown by SortedListView.Add(object), the string representation of the key (the list item object) is different (note the path of the executable).
Why SortList give me that exception?
To solve this I tried to implement a GetHashCode() for the underlying object, but without success:
public override int GetHashCode()
{
return (
base.GetHashCode() ^
mApplicationName.GetHashCode() ^
mApplicationPath.GetHashCode() ^
mCommandLine.GetHashCode() ^
mWorkingDirectory.GetHashCode()
);
}
If I understood you correctly, your purpose is just to get a view of you list, sorted by a property of the object.
Then, why use SortedList that requires unique Keys, when you could easily get your result using LINQ OrderBy (or if you're using .net 2.0 List.Sort()) ?
Hence, for example, your CreateSortView could be implemented in this way:
(omitting lock, try/finally and reference counting)
public IList<T> CreateSortView(string property)
{
IList<T> sortView;
if (mSortListViews.ContainsKey(property) == false)
{
// Create sorted view
sortView = this.OrderBy(x => x, new GenericPropertyComparer<T>(property)).ToList();
mSortListViews.Add(property, sortView);
}
else
{
sortView = mSortListViews[property];
}
return sortView;
}
With GenericPropertyComparer implemented as follows:
class GenericPropertyComparer<T> : IComparer<T>
{
public GenericPropertyComparer(string propertyName)
{
var property = typeof(T).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
if (property == null)
throw new ArgumentException("property doesn't specify a valid property");
if (property.CanRead == false)
throw new ArgumentException("property specify a write-only property");
if (property.PropertyType.GetInterface("IComparable") == null)
throw new ArgumentException("property type doesn't IComparable");
mSortingProperty = property;
}
public int Compare(T x, T y)
{
IComparable propX = (IComparable)mSortingProperty.GetValue(x, null);
IComparable propY = (IComparable)mSortingProperty.GetValue(y, null);
return (propX.CompareTo(propY));
}
private PropertyInfo mSortingProperty = null;
}
EDIT:
If you need to add/remove items from your sorted collection frequeltly, maybe use a SortedList would be better, but the problem with SortedList is that it needs unique keys, and in your case you can't assure that.
Anyway, you can use a custom sorted List that doesn't need unique values, look at the link below for a simple implementation:
Implementation of sorted IList<T> that doesn't require unique values
It seems to me it is a multithreading issue. I can't see what the Lock() function is doing in your code, but I think you will have more luck by surrounding the dictionary access code with a standard lock:
lock(this){
SortListView sortView;
if (mSortListViews.ContainsKey(property) == false) {
// Create sorted view
sortView = new SortListView(property, Count);
mSortListViews.Add(property, sortView);
foreach (T item in Items)
sortView.Add(item);
} else
sortView = mSortListViews[property];
sortView.ReferenceCount++;
}
and the same in the removing part.
Thanks to digEmAll's comment, I found a quick solution: The IComparer implementation shall return 0 only on objects really equals!
So:
public int Compare(object x, object y)
{
IComparable propX = (IComparable)mSortingProperty.GetValue(x, null);
IComparable propY = (IComparable)mSortingProperty.GetValue(y, null);
int compare;
if ((compare = propX.CompareTo(propY)) == 0) {
if (x.GetHashCode() < y.GetHashCode())
return (-1);
else if (x.GetHashCode() > y.GetHashCode())
return (+1);
else return (0);
} else
return (compare);
}

Categories