从类型集合中获取公共基类的最简单方法

我正在构建一个自定义属性网格,用于显示集合中项目的属性。 我想要做的是只显示网格中每个项目中常见的属性。 我假设最好的方法是找到集合中每种类型的公共基类并显示它的属性。 有没有更简单的方法? 你能给我一个最佳方法的代码示例吗?

您可以使用一种不断检查公共基类的方法来执行此操作。 我使用Type类的BaseClassfunction快速编写了这个。 您不必使用数组,列表或其他IEnumerable可以对此进行小的修改。

我测试了它:

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

得到了DbCommand的正确答案。 这是我的代码。

  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's 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's 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's always at least object return tested; } 

要从对象集合中获取公共属性,可以使用如下方法:

 public static String[] GetCommonPropertiesByName(Object[] objs) { List typeList = new List(Type.GetTypeArray(objs)); List propertyList = new List(); List individualPropertyList = new List(); 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(); } 

然后,一旦获得了要执行某些操作的属性的字符串,就可以使用集合中的任何对象并使用reflection按其字符串名称调用该属性。

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

类似地,可以修改GetCommonPropertiesByName方法中的代码以获取公共成员,方法,嵌套类型,字段等…

为了获得一组类型的最具体的公共基础而发布的代码存在一些问题。 特别是,当我将typeof(object)作为其中一种类型传递时,它会中断。 我相信以下更简单,更好(更好)。

 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; } 

我还测试过:

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

并获得typeof(DbCommand) 。 与:

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

并获得typeof(Compoment) 。 与:

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

并得到了typeof(MarshalByRefObject) 。 与

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

并得到typeof(object)

好,

您可以在类似于IComparable的界面中创建,而是将其称为IPropertyComparable,然后让实现它的类使用reflection来比较它们的属性名称,如此…

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

我会让你弄明白其余的。 无论如何它可能会更优雅,使用LINQ可能……

  • 马特

以下是从类型列表中获取公共属性集的方法:

 class TypeHandler { public static List GetCommonProperties(Type[] types) { Dictionary propertyCounts = new Dictionary(); 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 propertyNames = new List(); foreach (string name in propertyCounts.Keys) { if (propertyCounts[name] == types.Length) propertyNames.Add(name); } return propertyNames; } } 

这将遍历所有类型中的所有属性,并且最终只会出现与类型数相等的次数。

如果您更喜欢紧凑的LINQ查询,则可以使用以下等效表达式:

 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(); 

我使用这样的东西,但托尼的回答可能更好:

 internal class BaseFinder { public static Type FindBase(params Type[] types) { if (types == null) return null; if (types.Length == 0) return null; Dictionary> baseTypeMap = new Dictionary>(); // 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 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 longestBaseTree = baseTypeMap[typeWithLongestBaseTree]; for (int baseIndex = 0; baseIndex < longestBaseTree.Count;baseIndex++) { int commonBaseCount = 0; foreach (Type type in types) { IList baseTypes = baseTypeMap[type]; if (!baseTypes.Contains(longestBaseTree[baseIndex])) break; commonBaseCount++; } if (commonBaseCount == types.Length) return longestBaseTree[baseIndex]; } return null; } private static IList GetBaseTree(Type type) { List result = new List(); Type baseType = type.BaseType; do { result.Add(baseType); baseType = baseType.BaseType; } while (baseType != typeof(object)); return result; } }