2011-08-12

Pobranie wszystkich metod typu

Wszystkich czyli publicznych, chronionych, prywatnych. Także tych ukrytych. Pomijamy metody abstrakcyjne. Z nadpisanym metod wirtualnych zwracamy tylko te "najmłodsze" w hierarchii klas.

Możemy także zdecydować się czy odfiltrować metody generowane dla automatycznych właściwości.

Metody są zwracane w sposób posortowany najpierw po typie (od najmłodszego do bazowego), a dalej w kolejności występowania.

I znowu problemy. Tym razem przypuśćmy, że mamy metodę publiczną zdefiniowaną w klasie A, mamy klasę B wywodzącą się z A. Pobieramy tą metodę z typu A i z typu B, chcemy ustalić, że są takie same. Ich porównanie nic nie da. Ich ReflectedType są różne. Pozostaje więc ręcznie wykazać, że metody mają takie same sygnatury. Z całego dobrodziejswta MethodInfo decydujemy się nazwy i parametry. Niestety, okaże się parametrów też nie możemy porównać. Żeby było śmieszniej różnią się tylko MetadataToken. Z całego dobrodziejstwa ParameterInfo decydujemy się na porównanie ParameterType. Bez problemu radzimy sobie z rozróżnieniem parametrów z out, in, ref. Zgodnie z moimi testami wszystko działa. Ale nie wykluczam, że z uwagi na to, że do porównania nie biorę wszystkich elementów istnieją takie których porównanie zwróci fałszywy wynik.

Kod:
public 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<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;
    }
}

public static bool IsDerivedFrom(this MethodInfo a_mi, MethodInfo a_base,
    bool a_with_this = false)
{
    if (a_mi.Name != a_base.Name)
        return false;
    if (a_mi.DeclaringType == a_base.DeclaringType)
    {
        if (!a_mi.GetParameters().Select(p => p.ParameterType).SequenceEqual(
            a_base.GetParameters().Select(p => p.ParameterType)))
        {
            return false;
        }

        return a_with_this;
    }

    return a_mi.GetBaseDefinitions().Contains(a_base);
}

public static IEnumerable<MethodInfo> GetAllMethods(this Type a_type,
    bool a_include_autogenerated = false)
{
    List<MethodInfo> result = new List<MethodInfo>();

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

        MethodInfo[] methods = t.GetMethods(
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance);

        foreach (var method in methods.Reverse())
        {
            if (method.IsDefined(typeof(CompilerGeneratedAttribute), true) &&
                !a_include_autogenerated)
            {
                continue;
            }

            if (method.IsAbstract)
                continue;
            if (result.All(m => !m.IsDerivedFrom(method, true)))
            {
                result.Add(method);
                yield return method;
            }
        }
    }
}

Brak komentarzy:

Prześlij komentarz