Самый простой способ получить общий базовый класс из коллекции типов - PullRequest
4 голосов
/ 09 декабря 2008

Я строю сетку пользовательских свойств, которая отображает свойства элементов в коллекции. Я хочу показать только свойства в сетке, которые являются общими для каждого элемента. Я предполагаю, что лучший способ сделать это - найти общий базовый класс каждого типа в коллекции и отобразить его свойства. Есть ли более простой способ? Можете ли вы дать мне пример кода наилучшего подхода для этого?

Ответы [ 6 ]

3 голосов
/ 09 декабря 2008

Вы можете сделать это с помощью метода, который продолжает проверять общие базовые классы. Я написал это быстро, используя функцию BaseClass класса Type. Вам не нужно использовать массив, список или другой 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;
    }
2 голосов
/ 31 марта 2009

Код, опубликованный для получения наиболее конкретной общей базы для набора типов, имеет некоторые проблемы. В частности, он ломается, когда я передаю typeof (объект) как один из типов. Я считаю, что следующее проще и (лучше) правильно.

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).

2 голосов
/ 09 декабря 2008

Чтобы получить общие свойства из коллекции объектов, вы можете использовать метод, подобный этому:

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

Затем, когда у вас есть строка свойства, с которым вы хотите что-то сделать, вы можете взять любой из Объектов в коллекции и использовать рефлексию для вызова этого свойства по его строковому имени.

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

Аналогично, код в методе GetCommonPropertiesByName можно изменить, чтобы получить общие члены, методы, вложенные типы, поля и т. Д. *

0 голосов
/ 10 декабря 2008

Я использую что-то вроде этого, но ответ Тони, вероятно, лучше:

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 голосов
/ 09 декабря 2008

Вот способ получить общий набор свойств из списка типов:

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

Это перебирает все свойства во всех типах и заканчивается только теми, которые встречаются количество раз, равное количеству типов.

Если вы предпочитаете компактные запросы 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();
0 голосов
/ 09 декабря 2008

Ну

Вы можете создать интерфейс, похожий на IComparable, но вместо этого назвать его как IPropertyComparable, и тогда классы, которые его реализуют, используют отражение для сравнения имен своих свойств, как ...

public int Compare(T x, T y)
{
     PropertyInfo[] props = x.GetType().GetProperties();

     foreach(PropertyInfo info in props)
     {
          if(info.name == y.GetType().Name)
          ....
     }

     ...

Я позволю тебе разобраться с остальными. В любом случае это может быть немного элегантнее, возможно, используйте LINQ ...

  • Мэтт
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...