I have collections of entities that I need to sort depending on their dependencies to each other. Here's an example because it's fairly tough to explain:
public class A : I {
private B objB;
public B propB { get{ return objB; } }
// Some other fields and properties.
}
public class B : I { /* Some fields and properties. */ }
public class C : I {
private A objA;
public A propA { get{ return objA; } }
// Some other fields and properties.
}
public interface I {}
The thing is that I need to import data into collections of those types but I need to import in a certain order because if I import A objects first, I will not be able to link the corresponding B since it won't exist yet.
So what I'd like is to sort my collections in a way that all dependencies are imported in the right order (There are no circular dependencies). I can't figure out a linq statement that will do that though.
lists.OrderBy(l => l. ??? );
I was thinking maybe get a list typesList of all the types T of the List<T> in lists and somehow use reflection to count how many fields in T are in typesList but that seems... inefficient ?
EDIT: Realized the wording of my structure is a bit vague.
Basically lists is a List<List<I>>. Here's a results example:
List<List<I>> collections before:
-List<A>
-List<C>
-List<B>
List<List<I>> collections after:
-List<B> // B has 0 dependency to B or C.
-List<A> // A has 1 dependency to B.
-List<C> // C has 1 dependency to A.
The easiest way I know to do it is build up a new collection, as you build it up see if you see any known types in the class. If you do see a known type put it it just before the first sighting or put it at the end if no known types were found. Once you have that collection just enumerate the collection in reverse order and it will the items in reverse dependency order.
The below example is used like
var sortedLists = lists.OrderByTypeDependency();
How to implement the LINQ style extension method:
static class ExtensionMethods
{
public static IEnumerable<T> OrderByTypeDependency<T>(this IEnumerable<T> items)
{
LinkedList<T> knownItems = new LinkedList<T>();
foreach (var item in items)
{
var itemType = item.GetType();
var itemPropertyTypes =
itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.PropertyType);
var itemFieldTypes =
itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.FieldType);
//Create a set of all types internal to type we are checking on.
HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));
bool found = false;
for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
{
var knownItemType = knownItemNode.Value.GetType();
if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
{
knownItems.AddBefore(knownItemNode, item);
found = true;
break;
}
}
if (!found)
{
knownItems.AddLast(item);
}
}
//output the result in reverse order.
for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
{
yield return knownItemNode.Value;
}
}
}
EDIT: it was not clear if you where passing in a list of types or a list of objects. If you are passing in a list of types it is only a few small tweaks to the code, just drop the two .GetType() calls and switch from generics to only accepting IEnumerable<Type>
static class ExtensionMethods
{
public static IEnumerable<Type> OrderByTypeDependency(this IEnumerable<Type> items)
{
LinkedList<Type> knownItems = new LinkedList<Type>();
foreach (var item in items)
{
var itemType = item;
var itemPropertyTypes =
itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.PropertyType);
var itemFieldTypes =
itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.FieldType);
//Create a set of all types internal to type we are checking on.
HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));
bool found = false;
for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
{
var knownItemType = knownItemNode.Value;
if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
{
knownItems.AddBefore(knownItemNode, item);
found = true;
break;
}
}
if (!found)
{
knownItems.AddLast(item);
}
}
for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
{
yield return knownItemNode.Value;
}
}
}
UPDATE:
Per your update to the question, as long as the inner lists hold the same type of object in the list so we can just check the first item in the list to find it's type this modification of the original code will do the sorting you need.
static class ExtensionMethods
{
public static IEnumerable<T> OrderByTypeDependency<T>(this IEnumerable<T> outerList)
where T: IList
{
LinkedList<T> knownItems = new LinkedList<T>();
foreach (var innerList in outerList)
{
if(innerList.Count == 0)
continue;
var itemType = innerList[0].GetType();
var itemPropertyTypes = itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.PropertyType);
var itemFieldTypes = itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.FieldType);
//Create a set of all types internal to type we are checking on.
HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));
bool found = false;
for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
{
var knownItemType = knownItemNode.Value[0].GetType();
if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
{
knownItems.AddBefore(knownItemNode, innerList);
found = true;
break;
}
}
if (!found)
{
knownItems.AddLast(innerList);
}
}
for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
{
yield return knownItemNode.Value;
}
}
}
Related
Following is a class
public class Attribute
{
public string Name { get; set; }
public string Value { get; set; }
}
Following is the code in my main method
{
var test = new List<Attribute>();
test.Add(new Attribute { Name = "Don", Value = "21" });
test.Add(new Attribute { Value = "34", Name = "Karthik" });
var test1 = new List<Attribute>();
test1.Add(new Attribute { Name = "Don", Value = "21" });
test1.Add(new Attribute { Value = "34", Name = "Karthik" });
var obj = new Program();
var areEqual1 = obj.CompareList<List<Attribute>>(test, test1);
}
I have a ComapreList method
public bool CompareList<T>(T firstList, T secondList) where T : class
{
var list1 = firstList as IList<T>;
return true;
}
Now, list1 has null. I know that .net does not allow us to do this. But is there any other way where I can cast this generic list. My purpose is to compare each property value of these two list. I am using reflection to get the property but it works only if I can convert the firstlist/secondlist to something enumerable. if I directly use the name of the class in the IList<> (firstList as IList<Attribute>) it works, but not if I give <T>. Please help.
Just create method parameterized by type of lists items type. Even more, you can create method which compares any type of collections:
public bool CompareSequences<T> (IEnumerable<T> first, IEnumerable<T> second,
Comparer<T> comparer = null)
{
comparer = comparer ?? Comparer<T>.Default;
if (first == null)
throw new ArgumentNullException(nameof(first));
if (second == null)
throw new ArgumentNullException(nameof(second));
var firstIterator = first.GetEnumerator();
var secondIterator = second.GetEnumerator();
while(true)
{
bool firstHasItem = firstIterator.MoveNext();
bool secondHasItem = secondIterator.MoveNext();
if (firstHasItem != secondHasItem)
return false;
if (!firstHasItem && !secondHasItem)
return true;
if (comparer.Compare(firstIterator.Current, secondIterator.Current) != 0)
return false;
}
}
If collection items are primitive types, you can use default comparer. But if collections contain custom items, you need either IComparable to be implemented by collection items type:
public class Attribute : IComparable<Attribute>
{
public string Name { get; set; }
public string Value { get; set; }
public int CompareTo (Attribute other)
{
int result = Name.CompareTo(other.Name);
if (result == 0)
return Value.CompareTo(other.Value);
return result;
}
}
Or you can create and pass comparer instance. You can create comparer which is using reflection to compare fields/properties of some type. But it's not as simple as you might think - properties can be complex type or collections.
Usage:
var areEqual1 = obj.CompareSequences(test, test1);
If you don't need to compare objects with complex structure (which have inner collections and other custom objects) then you can use comparer like this one:
public class SimplePropertiesComparer<T> : Comparer<T>
{
public override int Compare (T x, T y)
{
Type type = typeof(T);
var flags = BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance;
foreach (var property in type.GetProperties(flags))
{
var propertyType = property.PropertyType;
if (!typeof(IComparable).IsAssignableFrom(propertyType))
throw new NotSupportedException($"{propertyType} props are not supported.");
var propertyValueX = (IComparable)property.GetValue(x);
var propertyValueY = (IComparable)property.GetValue(y);
if (propertyValueX == null && propertyValueY == null)
continue;
if (propertyValueX == null)
return -1;
int result = propertyValueX.CompareTo(property.GetValue(y));
if (result == 0)
continue;
return result;
}
return 0;
}
}
And pass it to sequence comparer
var equal = obj.CompareSequences(test, test1, new SimplePropertiesComparer<Attribute>());
Change the signature of your method and remove the then redundant cast:
public bool CompareList<T>(IList<T> firstList, IList<T> secondList) where T : class
{
var list1 = firstList as IList<T>; // Cast is not necessary any more
return true;
}
public bool CompareGenericLists<T, U>(List<T> list1, List<U> list2)
{
try
{
if (typeof(T).Equals(typeof(U)))
{
//For checking null lists
if (list1 == null && list2 == null)
return true;
if (list1 == null || list2 == null)
throw new Exception("One of the Lists is Null");
if (list1.Count.Equals(list2.Count))
{
Type type = typeof(T);
//For primitive lists
if (type.IsPrimitive)
{
int flag = 0;
for (int i = 0; i < list1.Count; i++)
{
if (list1.ElementAt(i).Equals(list2.ElementAt(i)))
flag++;
}
if (flag != list1.Count)
throw new Exception("Objects values are not same");
}
//For Reference List
else
{
for (int i = 0; i < list1.Count; i++)
{
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
string Object1Value = string.Empty;
string Object2Value = string.Empty;
Object1Value = type.GetProperty(property.Name).GetValue(list1.ElementAt(i)).ToString();
Object2Value = type.GetProperty(property.Name).GetValue(list2.ElementAt(i)).ToString();
if (Object1Value != Object2Value)
{
throw new Exception("Objects values are not same");
}
}
}
}
}
else
throw new Exception("Length of lists is not Same");
}
else
throw new Exception("Different type of lists");
}
catch(Exception ex)
{
throw ex;
}
return true;
}
this method can be used for both primitive and reference lists.try this method.It will compare type,counts,and members of lists.
Context:
I have a method to create a long Conjunction:
public static Conjunction GetLongConjunction()
{
Conjunction conjunction = new Conjunction();
conjunction.Add<Person>(p => p.Id > 0);
conjunction.Add<Person>(p => p.Age > 18);
Disjunction disjunction = new Disjunction();
disjunction.Add<Person>(p => p.Name == "John");
disjunction.Add<Person>(p => p.Name == "Alice");
conjunction.Add(disjunction);
// ...
return conjunction;
}
In another method, I'm using GetLongConjunction to get the conjunction:
public void AnotherMethod()
{
Conjunction newConjunction = GetLongConjunction();
// ...
}
The problem is: I want to edit (or remove) one condition in newConjunction.
What I tried:
I tried to get the List<NHibernate.Criterion.ICriterion> in criteria property from AbstractCriterion class. Conjunction extends it:
Conjunction: Junction: AbstractCriterion
But criteria is a private property, and I can't get it.
Question:
So, my question is:
How can I edit a NHibernate Junction? Is that possible?
Thanks!!
To keep it simple, you can use reflection:
public void AnotherMethod()
{
Conjunction newConjunction = GetLongConjunction();
IList<ICriterion> criteria = newConjunction.GetCriteria();
// Add or remove expressions
// var disjunction = (Disjunction) criteria.Last();
}
Using this extension methods:
public static class MyExtensions
{
public static IList<ICriterion> GetCriteria(this Junction juntion)
{
return juntion.GetPrivateFieldValue<IList<ICriterion>>("criteria");
}
public static T GetPrivateFieldValue<T>(this object obj, string propName)
{
if (obj == null) throw new ArgumentNullException("obj");
Type t = obj.GetType();
FieldInfo fi = null;
while (fi == null && t != null)
{
fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
t = t.BaseType;
}
if (fi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
return (T)fi.GetValue(obj);
}
}
Would like to be able to populate any properties of an object and search a collection for objects that match the given properties.
class Program
{
static List<Marble> marbles = new List<Marble> {
new Marble {Color = "Red", Size = 3},
new Marble {Color = "Green", Size = 4},
new Marble {Color = "Black", Size = 6}
};
static void Main()
{
var search1 = new Marble { Color = "Green" };
var search2 = new Marble { Size = 6 };
var results = SearchMarbles(search1);
}
public static IEnumerable<Marble> SearchMarbles(Marble search)
{
var results = from marble in marbles
//where ???
//Search for marbles with whatever property matches the populated properties of the parameter
//In this example it would return just the 'Green' marble
select marble;
return results;
}
public class Marble
{
public string Color { get; set; }
public int Size { get; set; }
}
}
Admittedly, it is interesting and take me time. First, you need to get all properties of search object which have value different with default value, this method is generic using reflection:
var properties = typeof (Marble).GetProperties().Where(p =>
{
var pType = p.PropertyType;
var defaultValue = pType.IsValueType
? Activator.CreateInstance(pType) : null;
var recentValue = p.GetValue(search);
return !recentValue.Equals(defaultValue);
});
Then you can use LINQ All to filter:
var results = marbles.Where(m =>
properties.All(p =>
typeof (Marble).GetProperty(p.Name)
.GetValue(m) == p.GetValue(search)));
P.s: This code has been tested
I am going to propose the generic solution which will work with any number of properties and with any object. It will also be usable in Linq-To-Sql context - it will translate well to sql.
First, start by defining function which will test if the given value is to be treated as a non-set, e.g:
static public bool IsDefault(object o)
{
return o == null || o.GetType().IsValueType && Activator.CreateInstance(o.GetType()).Equals(o);
}
Then, we will have a function which constructs a Lambda expression with test against the values of all set properties in search object:
static public Expression<Func<T, bool>> GetComparison<T>(T search)
{
var param = Expression.Parameter(typeof(T), "t");
var props = from p in typeof(T).GetProperties()
where p.CanRead && !IsDefault(p.GetValue(search, null))
select Expression.Equal(
Expression.Property(param, p.Name),
Expression.Constant(p.GetValue(search, null))
);
var expr = props.Aggregate((a, b) => Expression.AndAlso(a, b));
var lambda = Expression.Lambda<Func<T, bool>>(expr, param);
return lambda;
}
We can use it on any IQueryable:
public static IEnumerable<Marble> SearchMarbles (Marble search)
{
var results = marbles.AsQueryable().Where(GetComparison(search));
return results.AsEnumerable();
}
You can use a separate Filter class like this:
class Filter
{
public string PropertyName { get; set; }
public object PropertyValue { get; set; }
public bool Matches(Marble m)
{
var T = typeof(Marble);
var prop = T.GetProperty(PropertyName);
var value = prop.GetValue(m);
return value.Equals(PropertyValue);
}
}
You can use this Filter as follows:
var filters = new List<Filter>();
filters.Add(new Filter() { PropertyName = "Color", PropertyValue = "Green" });
//this is essentially the content of SearchMarbles()
var result = marbles.Where(m => filters.All(f => f.Matches(m)));
foreach (var r in result)
{
Console.WriteLine(r.Color + ", " + r.Size);
}
You could use DependencyProperties to get rid of typing the property name.
Assuming a property is unpopulated if it has the default value (i.e. Color == null and Size == 0):
var results = from marble in marbles
where (marble.Color == search.Color || search.Color == null)
&& (marble.Size == search.Size || search.Size == 0)
select marble;
You could override equals in your Marbles class
public override bool Equals(object obj)
{
var other = obj as Marble;
if (null == other) return false;
return other.Color == this.color && other.size == this.size; // (etc for your other porperties
}
and then you could search by
return marbles.Where(m => search == m);
Using reflection, this method will work on all types, regardless of how many or what type of properties they contain.
Will skip any properties that are not filled out (null for ref type, default value for value type). If it finds two properties that are filled out that do not match returns false. If all filled-out properties are equal returns true.
IsPartialMatch(object m1, object m2)
{
PropertyInfo[] properties = m1.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
object v1 = property.GetValue(m1, null);
object v2 = property.GetValue(m2, null);
object defaultValue = GetDefault(property.PropertyType);
if (v1.Equals(defaultValue) continue;
if (v2.Equals(defaultVAlue) continue;
if (!v1.Equals(v2)) return false;
}
return true;
}
To apply it to your example
public static IEnumerable<Marble> SearchMarbles(Marble search)
{
return marbles.Where(m => IsPartialMatch(m, search))
}
GetDefault() is method from this post, Programmatic equivalent of default(Type)
If you want to avoid targeting specific properties, you could use reflection. Start by defining a function that returns the default value of a type (see here for a simple solution, and here for something more elaborate).
Then, you can write a method on the Marble class, which takes an instance of Marble as a filter:
public bool MatchesSearch(Marble search) {
var t = typeof(Marble);
return !(
from prp in t.GetProperties()
//get the value from the search instance
let searchValue = prp.GetValue(search, null)
//check if the search value differs from the default
where searchValue != GetDefaultValue(prp.PropertyType) &&
//and if it differs from the current instance
searchValue != prp.GetValue(this, null)
select prp
).Any();
}
Then, the SearchMarbles becomes:
public static IEnumerable<Marble> SearchMarbles(Marble search) {
return
from marble in marbles
where marble.MatchesSearch(search)
select marble;
}
Given a type, a name and a signature, how can I do a member lookup of the member with name name and signature signature using the C# rules of 7.4 (the 7.4 is the chapter number from the C# Language Specification) (or at least part of them... Let's say I can live with an exact match, without conversions/casts) at runtime? I need to get a MethodInfo/PropertyInfo/... because then I have to use it with reflection (to be more exact I'm trying to build an Expression.ForEach builder (a factory able to create an Expression Tree that represent a foreach statement), and to be pixel-perfect with the C# foreach I have to be able to do duck-typing and search for a GetEnumerator method (in the collection), a Current property and a MoveNext method (in the enumerator), as written in 8.8.4)
The problem (an example of the problem)
class C1
{
public int Current { get; set; }
public object MoveNext()
{
return null;
}
}
class C2 : C1
{
public new long Current { get; set; }
public new bool MoveNext()
{
return true;
}
}
class C3 : C2
{
}
var m = typeof(C3).GetMethods(); // I get both versions of MoveNext()
var p = typeof(C3).GetProperties(); // I get both versions of Current
Clearly if I try typeof(C3).GetProperty("Current") I get an AmbiguousMatchException exception.
A similar but different problem is present with interfaces:
interface I0
{
int Current { get; set; }
}
interface I1 : I0
{
new long Current { get; set; }
}
interface I2 : I1, I0
{
new object Current { get; set; }
}
interface I3 : I2
{
}
Here if I try to do a typeof(I3).GetProperties() I don't get the Current property (and this is something known, see for example GetProperties() to return all properties for an interface inheritance hierarchy), but I can't simply flatten the interfaces, because then I wouldn't know who is hiding who.
I know that probably this problem is solved somewhere in the Microsoft.CSharp.RuntimeBinder namespace (declared in the Microsoft.CSharp assembly). This because I'm trying to use C# rules and member lookup is necessary when you have dynamic method invocation, but I haven't been able to find anything (and then what I would get would be an Expression or perhaps a direct invocation).
After some thought it's clear that there is something similar in the Microsoft.VisualBasic assembly. VB.NET supports late binding. It's in Microsoft.VisualBasic.CompilerServices.NewLateBinding, but it doesn't expose the late bounded methods.
(note: shadowing = hiding = new in method/property/event definition in C#)
No one responded, so I'll post the code I cooked in the meantime. I hate to post 400 lines of code, but I wanted to be complete. There are two main methods: GetVisibleMethods and GetVisibleProperties. They are extension methods of the Type class. They will return the public visible (non-shadowed/non-overridden) methods/properties of a type. They should even handle VB.NET assemblies (VB.NET normally uses hide-by-name shadowing instead of hidebysig as done by C#). They cache their result in two static collections (Methods and Properties). The code is for C# 4.0, so I'm using ConcurrentDictionary<T, U>. If you are using C# 3.5 you can replace it with a Dictionary<T, U> but you have to protect it with a lock () { } when you read from it and when you write to it.
How does it works? It works by recursion (I know that recursion is normally bad, but I hope no one will create a 1.000 level inheritance chain).
For "real" types (non-interfaces) it walks upward one level (using recursion, so this one level up could go one level up and so on) and, from the returned list of methods/properties, it removes the methods/properties it's overloading/hiding.
For interfaces it's a little more complex. Type.GetInterfaces() returns all the interfaces that are inherited, ignoring if they are directly inherited or indirectly inherited. For each of those interfaces a list of declared methods/properties is calculated (through recursion). This list is paired with a list of methods/properties that are hidden by the interface (the HashSet<MethodInfo>/HashSet<PropertyInfo>). These methods/properties hidden by one interface or another are removed from all the other methods/properties returned from interfaces (so that if you have I1 with Method1(int), I2 inheriting from I1 that redeclares Method1(int) and in doing so hides I1.Method1 and I3 inheriting from I1 and I2, the fact that I2 hides I1.Method1 will be applied to the methods returned from exploring I1, so removing I1.Method1(int) (this happens because I don't generate an inheritance map for interfaces, I simply look at what hides what)).
From the returned collections of methods/properties one can use Linq to find the looked for method/property. Note that with interfaces you could find more than one method/property with a given signature. An example:
interface I1
{
void Method1();
}
interface I2
{
void Method1();
}
interface I3 : I1, I2
{
}
I3 will return two Method1().
The code:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public static class TypeEx
{
/// <summary>
/// Type, Tuple<Methods of type, (for interfaces)methods of base interfaces shadowed>
/// </summary>
public static readonly ConcurrentDictionary<Type, Tuple<MethodInfo[], HashSet<MethodInfo>>> Methods = new ConcurrentDictionary<Type, Tuple<MethodInfo[], HashSet<MethodInfo>>>();
/// <summary>
/// Type, Tuple<Properties of type, (for interfaces)properties of base interfaces shadowed>
/// </summary>
public static readonly ConcurrentDictionary<Type, Tuple<PropertyInfo[], HashSet<PropertyInfo>>> Properties = new ConcurrentDictionary<Type, Tuple<PropertyInfo[], HashSet<PropertyInfo>>>();
public static MethodInfo[] GetVisibleMethods(this Type type)
{
if (type.IsInterface)
{
return (MethodInfo[])type.GetVisibleMethodsInterfaceImpl().Item1.Clone();
}
return (MethodInfo[])type.GetVisibleMethodsImpl().Clone();
}
public static PropertyInfo[] GetVisibleProperties(this Type type)
{
if (type.IsInterface)
{
return (PropertyInfo[])type.GetVisiblePropertiesInterfaceImpl().Item1.Clone();
}
return (PropertyInfo[])type.GetVisiblePropertiesImpl().Clone();
}
private static MethodInfo[] GetVisibleMethodsImpl(this Type type)
{
Tuple<MethodInfo[], HashSet<MethodInfo>> tuple;
if (Methods.TryGetValue(type, out tuple))
{
return tuple.Item1;
}
var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
if (type.BaseType == null)
{
Methods.TryAdd(type, Tuple.Create(methods, (HashSet<MethodInfo>)null));
return methods;
}
var baseMethods = type.BaseType.GetVisibleMethodsImpl().ToList();
foreach (var method in methods)
{
if (method.IsHideByName())
{
baseMethods.RemoveAll(p => p.Name == method.Name);
}
else
{
int numGenericArguments = method.GetGenericArguments().Length;
var parameters = method.GetParameters();
baseMethods.RemoveAll(p =>
{
if (!method.EqualSignature(numGenericArguments, parameters, p))
{
return false;
}
return true;
});
}
}
if (baseMethods.Count == 0)
{
Methods.TryAdd(type, Tuple.Create(methods, (HashSet<MethodInfo>)null));
return methods;
}
var methods3 = new MethodInfo[methods.Length + baseMethods.Count];
Array.Copy(methods, 0, methods3, 0, methods.Length);
baseMethods.CopyTo(methods3, methods.Length);
Methods.TryAdd(type, Tuple.Create(methods3, (HashSet<MethodInfo>)null));
return methods3;
}
private static Tuple<MethodInfo[], HashSet<MethodInfo>> GetVisibleMethodsInterfaceImpl(this Type type)
{
Tuple<MethodInfo[], HashSet<MethodInfo>> tuple;
if (Methods.TryGetValue(type, out tuple))
{
return tuple;
}
var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
var baseInterfaces = type.GetInterfaces();
if (baseInterfaces.Length == 0)
{
tuple = Tuple.Create(methods, new HashSet<MethodInfo>());
Methods.TryAdd(type, tuple);
return tuple;
}
var baseMethods = new List<MethodInfo>();
var baseMethodsTemp = new MethodInfo[baseInterfaces.Length][];
var shadowedMethods = new HashSet<MethodInfo>();
for (int i = 0; i < baseInterfaces.Length; i++)
{
var tuple2 = baseInterfaces[i].GetVisibleMethodsInterfaceImpl();
baseMethodsTemp[i] = tuple2.Item1;
shadowedMethods.UnionWith(tuple2.Item2);
}
for (int i = 0; i < baseInterfaces.Length; i++)
{
baseMethods.AddRange(baseMethodsTemp[i].Where(p => !shadowedMethods.Contains(p)));
}
foreach (var method in methods)
{
if (method.IsHideByName())
{
baseMethods.RemoveAll(p =>
{
if (p.Name == method.Name)
{
shadowedMethods.Add(p);
return true;
}
return false;
});
}
else
{
int numGenericArguments = method.GetGenericArguments().Length;
var parameters = method.GetParameters();
baseMethods.RemoveAll(p =>
{
if (!method.EqualSignature(numGenericArguments, parameters, p))
{
return false;
}
shadowedMethods.Add(p);
return true;
});
}
}
if (baseMethods.Count == 0)
{
tuple = Tuple.Create(methods, shadowedMethods);
Methods.TryAdd(type, tuple);
return tuple;
}
var methods3 = new MethodInfo[methods.Length + baseMethods.Count];
Array.Copy(methods, 0, methods3, 0, methods.Length);
baseMethods.CopyTo(methods3, methods.Length);
tuple = Tuple.Create(methods3, shadowedMethods);
Methods.TryAdd(type, tuple);
return tuple;
}
private static PropertyInfo[] GetVisiblePropertiesImpl(this Type type)
{
Tuple<PropertyInfo[], HashSet<PropertyInfo>> tuple;
if (Properties.TryGetValue(type, out tuple))
{
return tuple.Item1;
}
var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
if (type.BaseType == null)
{
Properties.TryAdd(type, Tuple.Create(properties, (HashSet<PropertyInfo>)null));
return properties;
}
var baseProperties = type.BaseType.GetVisiblePropertiesImpl().ToList();
foreach (var property in properties)
{
if (property.IsHideByName())
{
baseProperties.RemoveAll(p => p.Name == property.Name);
}
else
{
var indexers = property.GetIndexParameters();
baseProperties.RemoveAll(p =>
{
if (!property.EqualSignature(indexers, p))
{
return false;
}
return true;
});
}
}
if (baseProperties.Count == 0)
{
Properties.TryAdd(type, Tuple.Create(properties, (HashSet<PropertyInfo>)null));
return properties;
}
var properties3 = new PropertyInfo[properties.Length + baseProperties.Count];
Array.Copy(properties, 0, properties3, 0, properties.Length);
baseProperties.CopyTo(properties3, properties.Length);
Properties.TryAdd(type, Tuple.Create(properties3, (HashSet<PropertyInfo>)null));
return properties3;
}
private static Tuple<PropertyInfo[], HashSet<PropertyInfo>> GetVisiblePropertiesInterfaceImpl(this Type type)
{
Tuple<PropertyInfo[], HashSet<PropertyInfo>> tuple;
if (Properties.TryGetValue(type, out tuple))
{
return tuple;
}
var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
var baseInterfaces = type.GetInterfaces();
if (baseInterfaces.Length == 0)
{
tuple = Tuple.Create(properties, new HashSet<PropertyInfo>());
Properties.TryAdd(type, tuple);
return tuple;
}
var baseProperties = new List<PropertyInfo>();
var basePropertiesTemp = new PropertyInfo[baseInterfaces.Length][];
var shadowedProperties = new HashSet<PropertyInfo>();
for (int i = 0; i < baseInterfaces.Length; i++)
{
var tuple2 = baseInterfaces[i].GetVisiblePropertiesInterfaceImpl();
basePropertiesTemp[i] = tuple2.Item1;
shadowedProperties.UnionWith(tuple2.Item2);
}
for (int i = 0; i < baseInterfaces.Length; i++)
{
baseProperties.AddRange(basePropertiesTemp[i].Where(p => !shadowedProperties.Contains(p)));
}
foreach (var property in properties)
{
if (property.IsHideByName())
{
baseProperties.RemoveAll(p =>
{
if (p.Name == property.Name)
{
shadowedProperties.Add(p);
return true;
}
return false;
});
}
else
{
var indexers = property.GetIndexParameters();
baseProperties.RemoveAll(p =>
{
if (!property.EqualSignature(indexers, p))
{
return false;
}
shadowedProperties.Add(p);
return true;
});
}
}
if (baseProperties.Count == 0)
{
tuple = Tuple.Create(properties, shadowedProperties);
Properties.TryAdd(type, tuple);
return tuple;
}
var properties3 = new PropertyInfo[properties.Length + baseProperties.Count];
Array.Copy(properties, 0, properties3, 0, properties.Length);
baseProperties.CopyTo(properties3, properties.Length);
tuple = Tuple.Create(properties3, shadowedProperties);
Properties.TryAdd(type, tuple);
return tuple;
}
private static bool EqualSignature(this MethodInfo method1, int numGenericArguments1, ParameterInfo[] parameters1, MethodInfo method2)
{
// To shadow by signature a method must have same name, same number of
// generic arguments, same number of parameters and same parameters' type
if (method1.Name != method2.Name)
{
return false;
}
if (numGenericArguments1 != method2.GetGenericArguments().Length)
{
return false;
}
var parameters2 = method2.GetParameters();
if (!parameters1.EqualParameterTypes(parameters2))
{
return false;
}
return true;
}
private static bool EqualSignature(this PropertyInfo property1, ParameterInfo[] indexers1, PropertyInfo property2)
{
// To shadow by signature a property must have same name,
// same number of indexers and same indexers' type
if (property1.Name != property2.Name)
{
return false;
}
var parameters2 = property1.GetIndexParameters();
if (!indexers1.EqualParameterTypes(parameters2))
{
return false;
}
return true;
}
private static bool EqualParameterTypes(this ParameterInfo[] parameters1, ParameterInfo[] parameters2)
{
if (parameters1.Length != parameters2.Length)
{
return false;
}
for (int i = 0; i < parameters1.Length; i++)
{
if (parameters1[i].IsOut != parameters2[i].IsOut)
{
return false;
}
if (parameters1[i].ParameterType.IsGenericParameter)
{
if (!parameters2[i].ParameterType.IsGenericParameter)
{
return false;
}
if (parameters1[i].ParameterType.GenericParameterPosition != parameters2[i].ParameterType.GenericParameterPosition)
{
return false;
}
}
else if (parameters1[i].ParameterType != parameters2[i].ParameterType)
{
return false;
}
}
return true;
}
private static bool IsHideByName(this MethodInfo method)
{
if (!method.Attributes.HasFlag(MethodAttributes.HideBySig) && (!method.Attributes.HasFlag(MethodAttributes.Virtual) || method.Attributes.HasFlag(MethodAttributes.NewSlot)))
{
return true;
}
return false;
}
private static bool IsHideByName(this PropertyInfo property)
{
var get = property.GetGetMethod();
if (get != null && get.IsHideByName())
{
return true;
}
var set = property.GetSetMethod();
if (set != null && set.IsHideByName())
{
return true;
}
return false;
}
}
Use overloaded method with BindingFlags parameter.
GetProperties(BindingFlags.DeclaredOnly)
I am writing a Clone method using reflection. How do I detect that a property is an indexed property using reflection? For example:
public string[] Items
{
get;
set;
}
My method so far:
public static T Clone<T>(T from, List<string> propertiesToIgnore) where T : new()
{
T to = new T();
Type myType = from.GetType();
PropertyInfo[] myProperties = myType.GetProperties();
for (int i = 0; i < myProperties.Length; i++)
{
if (myProperties[i].CanWrite && !propertiesToIgnore.Contains(myProperties[i].Name))
{
myProperties[i].SetValue(to,myProperties[i].GetValue(from,null),null);
}
}
return to;
}
if (propertyInfo.GetIndexParameters().Length > 0)
{
// Property is an indexer
}
Sorry, but
public string[] Items { get; set; }
is not an indexed property, it's merely of an array type!
However the following is:
public string this[int index]
{
get { ... }
set { ... }
}
What you want is the GetIndexParameters() method. If the array that it returns has more than 0 items, that means it's an indexed property.
See the MSDN documentation for more details.
If you call property.GetValue(obj,null), and the property IS indexed, then you will get a parameter count mismatch exception. Better to check whether the property is indexed using GetIndexParameters() and then decide what to do.
Here is some code that worked for me:
foreach (PropertyInfo property in obj.GetType().GetProperties())
{
object value = property.GetValue(obj, null);
if (value is object[])
{
....
}
}
P.S. .GetIndexParameters().Length > 0) works for the case described in this article: http://msdn.microsoft.com/en-us/library/b05d59ty.aspx
So if you care about the property named Chars for a value of type string, use that, but it does not work for most of the arrays I was interested in, including, I am pretty sure, a string array from the original question.
You can convert the indexer to IEnumerable
public static IEnumerable<T> AsEnumerable<T>(this object o) where T : class {
var list = new List<T>();
System.Reflection.PropertyInfo indexerProperty = null;
foreach (System.Reflection.PropertyInfo pi in o.GetType().GetProperties()) {
if (pi.GetIndexParameters().Length > 0) {
indexerProperty = pi;
break;
}
}
if (indexerProperty.IsNotNull()) {
var len = o.GetPropertyValue<int>("Length");
for (int i = 0; i < len; i++) {
var item = indexerProperty.GetValue(o, new object[]{i});
if (item.IsNotNull()) {
var itemObject = item as T;
if (itemObject.IsNotNull()) {
list.Add(itemObject);
}
}
}
}
return list;
}
public static bool IsNotNull(this object o) {
return o != null;
}
public static T GetPropertyValue<T>(this object source, string property) {
if (source == null)
throw new ArgumentNullException("source");
var sourceType = source.GetType();
var sourceProperties = sourceType.GetProperties();
var properties = sourceProperties
.Where(s => s.Name.Equals(property));
if (properties.Count() == 0) {
sourceProperties = sourceType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic);
properties = sourceProperties.Where(s => s.Name.Equals(property));
}
if (properties.Count() > 0) {
var propertyValue = properties
.Select(s => s.GetValue(source, null))
.FirstOrDefault();
return propertyValue != null ? (T)propertyValue : default(T);
}
return default(T);
}