The easiest way to get a common base class from a set of types

I am creating a custom property grid that displays the properties of elements in a collection. What I want to do is show only the properties in the grid that are common to each element. I guess the best way to do this is to find a common base class of each type in the collection and display its properties. Is there an easier way? Can you give me an example code of the best approach for this?

+3
reflection c # linq
Dec 09 '08 at 16:44
source share
6 answers

You can do this with a method that checks for common base classes. I quickly wrote this using the BaseClass function of the Type class. You do not need to use an array, list, or other IEnumerable can work with small changes.

I tested it with

static void Main(string[] args) { Console.WriteLine("Common Types: " + GetCommonBaseClass(new Type[] {typeof(OleDbCommand), typeof(OdbcCommand), typeof(SqlCommand)}).ToString()); } 

And got the correct answer from DbCommand. Here is my code.

  static Type GetCommonBaseClass(Type[] types) { if (types.Length == 0) return (typeof(object)); else if (types.Length == 1) return (types[0]); // Copy the parameter so we can substitute base class types in the array without messing up the caller Type[] temp = new Type[types.Length]; for (int i = 0; i < types.Length; i++) { temp[i] = types[i]; } bool checkPass = false; Type tested = null; while (!checkPass) { tested = temp[0]; checkPass = true; for (int i = 1; i < temp.Length; i++) { if (tested.Equals(temp[i])) continue; else { // If the tested common basetype (current) is the indexed type base type // then we can continue with the test by making the indexed type to be its base type if (tested.Equals(temp[i].BaseType)) { temp[i] = temp[i].BaseType; continue; } // If the tested type is the indexed type base type, then we need to change all indexed types // before the current type (which are all identical) to be that base type and restart this loop else if (tested.BaseType.Equals(temp[i])) { for (int j = 0; j <= i - 1; j++) { temp[j] = temp[j].BaseType; } checkPass = false; break; } // The indexed type and the tested type are not related // So make everything from index 0 up to and including the current indexed type to be their base type // because the common base type must be further back else { for (int j = 0; j <= i; j++) { temp[j] = temp[j].BaseType; } checkPass = false; break; } } } // If execution has reached here and checkPass is true, we have found our common base type, // if checkPass is false, the process starts over with the modified types } // There always at least object return tested; } 
+3
Dec 09 '08 at 17:13
source share

To get common properties from a collection of objects, you can use this method:

 public static String[] GetCommonPropertiesByName(Object[] objs) { List<Type> typeList = new List<Type>(Type.GetTypeArray(objs)); List<String> propertyList = new List<String>(); List<String> individualPropertyList = new List<String>(); foreach (Type type in typeList) { foreach (PropertyInfo property in type.GetProperties()) { propertyList.Add(property.Name); } } propertyList = propertyList.Distinct().ToList(); foreach (Type type in typeList) { individualPropertyList.Clear(); foreach (PropertyInfo property in type.GetProperties()) { individualPropertyList.Add(property.Name); } propertyList = propertyList.Intersect(individualPropertyList).ToList(); } return propertyList.ToArray(); } 

Then, as soon as you have a property string with which you want to do something, you can take any of the objects in the collection and use reflection to call this property by its string name.

 PropertyInfo p = t.GetType().GetProperty("some Property String Name"); p.GetValue(t, null); p.SetValue(t, someNewValue, null); 

Similarly, the code in the GetCommonPropertiesByName method can be modified to obtain common elements, methods, nested types, fields, etc.

+2
Dec 09 '08 at 18:27
source share

The code sent to get the most specific common base for a set of types has some problems. In particular, it breaks when I pass typeof (object) as one of the types. I believe the following is simpler and (better) correct.

 public static Type GetCommonBaseClass (params Type[] types) { if (types.Length == 0) return typeof(object); Type ret = types[0]; for (int i = 1; i < types.Length; ++i) { if (types[i].IsAssignableFrom(ret)) ret = types[i]; else { // This will always terminate when ret == typeof(object) while (!ret.IsAssignableFrom(types[i])) ret = ret.BaseType; } } return ret; } 

I also tested:

 Type t = GetCommonBaseClass(typeof(OleDbCommand), typeof(OdbcCommand), typeof(SqlCommand)); 

And got typeof(DbCommand) . And with the help of:

 Type t = GetCommonBaseClass(typeof(OleDbCommand), typeof(OdbcCommand), typeof(SqlCommand), typeof(Component)); 

And got typeof(Compoment) . And with the help of:

 Type t = GetCommonBaseClass(typeof(OleDbCommand), typeof(OdbcCommand), typeof(SqlCommand), typeof(Component), typeof(Component).BaseType); 

And got typeof(MarshalByRefObject) . And with

 Type t = GetCommonBaseClass(typeof(OleDbCommand), typeof(OdbcCommand), typeof(SqlCommand), typeof(Component), typeof(Component).BaseType, typeof(int)); 

And got typeof(object) .

+2
Mar 31 '09 at 16:26
source share

Well,

You can create in an interface like IComparable, but instead call it something like IPropertyComparable, and then use the classes that implement it using reflection to compare their property names like this ...

 public int Compare(T x, T y) { PropertyInfo[] props = x.GetType().GetProperties(); foreach(PropertyInfo info in props) { if(info.name == y.GetType().Name) .... } ... 

I'll let you figure out the rest. In any case, maybe it will be a little more elegant, maybe LINQ ...

  • Matt
0
Dec 09 '08 at 17:00
source share

Here is a way to get a common set of properties from a list of types:

 class TypeHandler { public static List<string> GetCommonProperties(Type[] types) { Dictionary<string, int> propertyCounts = new Dictionary<string, int>(); foreach (Type type in types) { foreach (PropertyInfo info in type.GetProperties()) { string name = info.Name; if (!propertyCounts.ContainsKey(name)) propertyCounts.Add(name, 0); propertyCounts[name]++; } } List<string> propertyNames = new List<string>(); foreach (string name in propertyCounts.Keys) { if (propertyCounts[name] == types.Length) propertyNames.Add(name); } return propertyNames; } } 

This iterates over all properties in all types and only ends up happening several times equal to the number of types.

If you prefer compact LINQ queries, you can use the following equivalent expression:

 return (from t in types from p in t.GetProperties() group p by p.Name into pg where pg.Count() == types.Length select pg.Key).ToList(); 
0
Dec 09 '08 at 18:02
source share

I use something similar, but Tony's answer is probably better:

 internal class BaseFinder { public static Type FindBase(params Type[] types) { if (types == null) return null; if (types.Length == 0) return null; Dictionary<Type, IList<Type>> baseTypeMap = new Dictionary<Type,IList<Type>>(); // get all the base types and note the one with the longest base tree int maxBaseCount = 0; Type typeWithLongestBaseTree = null; foreach (Type type in types) { IList<Type> baseTypes = GetBaseTree(type); if (baseTypes.Count > maxBaseCount) { typeWithLongestBaseTree = type; maxBaseCount = baseTypes.Count; } baseTypeMap.Add(type, baseTypes); } // walk down the tree until we get to a common base type IList<Type> longestBaseTree = baseTypeMap[typeWithLongestBaseTree]; for (int baseIndex = 0; baseIndex < longestBaseTree.Count;baseIndex++) { int commonBaseCount = 0; foreach (Type type in types) { IList<Type> baseTypes = baseTypeMap[type]; if (!baseTypes.Contains(longestBaseTree[baseIndex])) break; commonBaseCount++; } if (commonBaseCount == types.Length) return longestBaseTree[baseIndex]; } return null; } private static IList<Type> GetBaseTree(Type type) { List<Type> result = new List<Type>(); Type baseType = type.BaseType; do { result.Add(baseType); baseType = baseType.BaseType; } while (baseType != typeof(object)); return result; } } 
0
Dec 10 '08 at 11:50
source share



All Articles