Самый простой способ, чтобы получить общий базовый класс из коллекции типов

голоса
3

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

Задан 09/12/2008 в 17:44
источник пользователем
На других языках...                            


6 ответов

голоса
3

Вы можете сделать это с помощью метода, который держит проверки общих базовых классов. Я написал это, быстро, используя функцию 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;
    }
Ответил 09/12/2008 в 18:13
источник пользователем

голоса
2

Код размещен, чтобы получить самый специфическую общую базу для набора типов имеют некоторые проблемы. В частности, он ломает, когда я прохожу 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).

Ответил 31/03/2009 в 17:26
источник пользователем

голоса
2

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

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

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

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

Кроме того , код в GetCommonPropertiesByNameметод может быть изменен , чтобы получить общие элементы, методы, вложенные типы, поля, и т.д. ...

Ответил 09/12/2008 в 19:27
источник пользователем

голоса
0

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

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;
    }
}
Ответил 10/12/2008 в 12:50
источник пользователем

голоса
0

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

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();
Ответил 09/12/2008 в 19:02
источник пользователем

голоса
0

Что ж,

Вы можете создать в интерфейс, похожий на 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 может быть ...

  • Matt
Ответил 09/12/2008 в 18:00
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more