maneira mais fácil de obter uma classe base comum de uma coleção de tipos

votos
3

Estou construindo uma grade de propriedade personalizada que mostra as propriedades de itens em uma coleção. O que eu quero fazer é mostrar apenas as propriedades na grade que são comuns entre cada item. Estou assumindo a melhor maneira de fazer isso seria para encontrar a classe base comum de cada tipo na coleção e exibir suas propriedades. Existe alguma maneira mais fácil? Você pode me dar um exemplo de código do melhor abordagem para fazer isso?

Publicado 09/12/2008 em 17:44
fonte usuário
Em outras línguas...                            


6 respostas

votos
3

Você pode fazer isso com um método que mantém a verificação de classes de base comuns. Eu escrevi isso, rapidamente, usando o recurso ClasseBase da classe Type. Você não tem que usar uma matriz, uma lista ou outro IEnumerable pode trabalhar com pequenas modificações a este.

Eu testei com:

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

E tem a resposta certa de DbCommand. Aqui está o meu código.

    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;
    }
Respondeu 09/12/2008 em 18:13
fonte usuário

votos
2

O código postado para obter a base comum mais específico para um conjunto de tipos tem alguns problemas. Em particular, ele rompe quando passar typeof (objecto) como um dos tipos. Eu acredito que o seguinte é mais simples e (melhor) correta.

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

Eu também testado com:

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

E tenho typeof(DbCommand). E com:

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

E tenho typeof(Compoment). E com:

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

E tenho typeof(MarshalByRefObject). E com

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

E tenho typeof(object).

Respondeu 31/03/2009 em 17:26
fonte usuário

votos
2

Para obter as propriedades comuns de uma coleção de objetos, você pode usar um método como este:

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

Então, uma vez que você tem a corda de um imóvel que você quer fazer algo com, você pode tomar qualquer um dos objetos na coleção e uso de reflexão para chamar essa propriedade pelo seu Nome da cadeia.

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

Da mesma forma, o código no GetCommonPropertiesByNamemétodo pode ser modificado para obter os membros comuns, métodos, tipos aninhados, campos, etc ...

Respondeu 09/12/2008 em 19:27
fonte usuário

votos
0

Eu uso algo assim, mas a resposta de Tony é provavelmente melhor:

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;
    }
}
Respondeu 10/12/2008 em 12:50
fonte usuário

votos
0

Aqui está uma maneira de obter o conjunto comum de propriedades de uma lista de tipos:

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

A itera sobre todas as propriedades em todos os tipos e só termina-se com aquelas que ocorrem um número de vezes igual ao número de tipos.

Se preferir consultas LINQ compactos você pode usar a seguinte expressão equivalente:

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();
Respondeu 09/12/2008 em 19:02
fonte usuário

votos
0

Bem,

Você poderia criar na interface semelhante ao IComparable mas chamá-lo de algo como IPropertyComparable e depois ter as classes que implementam-lo usar a reflexão para comparar os seus nomes de propriedade como assim ...

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

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

     ...

Eu vou deixar você descobrir o resto. Ele provavelmente poderia ser um pouco mais elegante de qualquer maneira, usar LINQ talvez ...

  • Matt
Respondeu 09/12/2008 em 18:00
fonte usuário

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