2011-08-11

Pobranie wszystkich właściwości i pól typu

Wszystkich czyli także tych ukrytych, prywatnych, z typu i wszystkich podtypów i co ważniejsze unikalnych. W przypadku właściwości nadpisanych wirtualnych zwracamy tylko jedną. Pomijamy właściwości abstrakcyjne.

Zarówno właściwości jak i pola staramy się zwrócić posortowane według hierarchii typów, a następnie posortowane według tego jak występują w Typie, co odzwierciedla ich porządek w kodzie.

W przypadku pól istnieje możliwość odfiltrowania pól dodanych przez właściwości automatyczne.

Główny problem to jak sprawdzić, że dana właściwość wywodzi się z innej. Nie ma czegoś takiego jak PropertyInfo.BaseProperty. Sprawdzenia dokonujemy dla metody ustawiającej i pobierającej właściwości. Przy czym nie wystarczy zwykłe porównanie z metodą bazową. Może się zdarzyć jeśli użyjemy właściwości automatycznych, że w hierarchii klas A->B->C, gdzie A ma właściwość abstrakcyjną, w B i C ją nadpisujemy to metody B i C jako bazową wskazują na A.

Sprawdzenie czy dwie właściwości są takie same też nie jest proste. Ze względu na możliwe różne ReflectedType porównanie wprost nie działa tak jak powinno. Porównujemy więc nazwę, typ właściwości i jej parametry indeksowe. Porównania parametrów indeksowych znowu nie możemy wprost, gdyż mogą się one różnić wartością MetadataToken. Porównujemy więc ich typy. Ponieważ do porównania nie bierzemy wszystkiego tylko kilka elementów nie jestem pewny na 100%, czy są jakieś przypadki które będą zakwalifikowane jako false positive albo false nagative.

private static IEnumerable<methodinfo> GetBaseDefinitions(this MethodInfo a_mi,
    bool a_with_this = false)
{
    if (a_with_this)
        yield return a_mi;

    MethodInfo t = a_mi;

    while ((t.GetBaseDefinition() != null) && (t.GetBaseDefinition() != t))
    {
        t = t.GetBaseDefinition();
        yield return t;
    }
}

private static bool ContainsAny<t>(this IEnumerable<t> a_enumerable, 
    IEnumerable<t> a_values)
{
    return a_enumerable.Intersect(a_values).Any();
}

private static bool IsAbstract(this PropertyInfo a_pi)
{
    if (a_pi.GetAccessors(true).Length == 0)
        return false;
    return a_pi.GetAccessors(true)[0].IsAbstract;
}

private static bool IsDerivedFrom(this PropertyInfo a_pi, PropertyInfo a_base,
    bool a_with_this = false)
{
    if (a_pi.Name != a_base.Name)
        return false;
    if (a_pi.PropertyType != a_base.PropertyType)
        return false;
    if (!a_pi.GetIndexParameters().Select(p => p.ParameterType).SequenceEqual(
        a_base.GetIndexParameters().Select(p => p.ParameterType)))
    {
        return false;
    }
    if (a_pi.DeclaringType == a_base.DeclaringType)
        return a_with_this;

    MethodInfo m1 = a_pi.GetGetMethod(true);
    MethodInfo m3 = a_base.GetGetMethod(true);

    if ((m1 != null) && (m3 != null))
    {
        if (m1.GetBaseDefinitions().ContainsAny(m3.GetBaseDefinitions(true)))
            return true;
    }
    else if ((m1 != null) || (m3 != null))
        return false;

    MethodInfo m2 = a_pi.GetSetMethod(true);
    MethodInfo m4 = a_base.GetSetMethod(true);

    if ((m2 != null) && (m4 != null))
    {
        if (m2.GetBaseDefinitions().ContainsAny(m4.GetBaseDefinitions(true)))
            return true;
    }
    else if ((m2 != null) || (m4 != null))
        return false;

    return false;
}

private static IEnumerable<type> GetBaseTypes(this Type a_type,
    bool a_with_this = false)
{
    if (a_with_this)
        yield return a_type;

    Type t = a_type;

    while (t.BaseType != null)
    {
        t = t.BaseType;
        yield return t;
    }
}

public static IEnumerable<propertyinfo> GetAllProperties(this Type type)
{
    List<propertyinfo> result = new List<propertyinfo>();

    foreach (var t in type.GetBaseTypes(true))
    {
        if (t == typeof(Object))
            break;
        if (t == typeof(ValueType))
            break;

        PropertyInfo[] type_props = t.GetProperties(
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance);

        foreach (var poss_prop in type_props.Reverse())
        {
            if (poss_prop.IsAbstract())
                continue;

            if (result.All(prop => !prop.IsDerivedFrom(poss_prop, true)))
                result.Add(poss_prop);
        }
    }

    result.Reverse();
    return result;
}

public static IEnumerable<fieldinfo> GetAllFields(this Type type,
    bool a_filter_autogenerated = true)
{
    foreach (var t in type.GetBaseTypes(true).Reverse())
    {
        if (t == typeof(Object))
            continue;
        if (t == typeof(ValueType))
            continue;

        FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public |
            BindingFlags.NonPublic);

        foreach (var field in fields)
        {
            if (field.IsDefined(typeof(CompilerGeneratedAttribute), false) &&
                a_filter_autogenerated)
            {
                continue;
            }

            if (field.DeclaringType == t)
                yield return field;
        }
    }
}

Brak komentarzy:

Prześlij komentarz