BattletechModders / IRBTModUtils

General utility classes and common code shared across all my BattleTech mods
MIT License
6 stars 4 forks source link

ReflectionTypeLoadException thrown by GetAllTypesThatImplementInterface caused by certain type of derived classes #4

Closed toasterkun closed 3 years ago

toasterkun commented 3 years ago

I'm sorry for my English. It's not my first language.

I think the method GetAllTypesThatImplementInterface in ModInit may cause some problem if there's a Mod that extends a Class defined in another assembly when that assembly is not also referenced in IRBTModUtils (reference). The problem stems from the call to GetTypes which can throw a ReflectionTypeLoadException and causes ModTek to crash (please see attached ModTek log). This problem also occurs in Clever Girl which I believe uses the exact same code.

private static IEnumerable<Type> GetAllTypesThatImplementInterface<T>()
{
    var targetType = typeof(T);
    return AppDomain.CurrentDomain.GetAssemblies()
        .Where(a => !a.IsDynamic)
        .SelectMany(s => s.GetTypes())
        .Where(p => !p.IsInterface && !p.IsAbstract)
        .Where(p => targetType.IsAssignableFrom(p));
}

I can confirm that if I remove one of my mod that has such derived class as described in that Stackoverflow article, both IRBTM and Clever Girl works without any problem.

Based on the reference provided above, after recompiling IRBTMod and Clever girl with the following changes, it seems to work even with such derived classes in one of the active mods. Unfortunately, I don't know how to test whether the Mech Move Modifiers feature in IRBTM (and the similar feature in Clever Girl) is still working as you intended to be with these changes.

public static class TypeLoaderExtensions
    {
        public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
        {
            if (assembly == null) throw new ArgumentNullException("assembly");
            try
            {
                return assembly.GetTypes();
            }
            catch (ReflectionTypeLoadException e)
            {
                return e.Types.Where(t => t != null);
            }
        }
    }
private static IEnumerable<Type> GetAllTypesThatImplementInterface<T>()
{
    var targetType = typeof(T);
    return AppDomain.CurrentDomain.GetAssemblies()
        .Where(a => !a.IsDynamic)
        .SelectMany(s => s.GetLoadableTypes())
        .Where(p => !p.IsInterface && !p.IsAbstract)
        .Where(p => targetType.IsAssignableFrom(p));
}

I'm sorry if these "fixes" might not actually be the correct solution.

thx:) ModTek.log

toasterkun commented 3 years ago

Found out that my reference to NLog was the cause of the ReflectionTypeLoadException from GetTypes. Issue closed as removing that reference can also be used to solve this problem without the proposed code change.

thx :D