How to prevent MemberInfo.IsDefined from throwing FileNotFoundException on irrelevant attributes?

对着背影说爱祢 提交于 2019-12-11 03:21:19

问题


My project references TypesDefinitionAssembly with type SomeType, which is marked by attributes XSerializationOptions from XSerializationLibrary and YSerializationOptions from YSerializationLibrary.

Obviously, to check whether SomeType is marked by XSerializationOptions, I need to reference XSerializationLibrary as well. However, I don't want to reference YSerializationLibrary (which might even not be available).

Currently, the call to

typeof(SomeType).IsDefined(typeof(XSerializationOptions))

fails because IsDefined, for some reason, walks through all the attributes and tries to resolve all their types. The exception looks like:

System.IO.FileNotFoundException: Could not load file or assembly 'YSerializationLibrary, Version=1.2.3.4, Culture=neutral, PublicKeyToken=0123456789abcdef' or one of its dependencies. The system cannot find the file specified.
   at System.ModuleHandle.ResolveType(RuntimeModule module, Int32 typeToken, IntPtr* typeInstArgs, Int32 typeInstCount, IntPtr* methodInstArgs, Int32 methodInstCount, ObjectHandleOnStack type)
   at System.ModuleHandle.ResolveTypeHandleInternal(RuntimeModule module, Int32 typeToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext)
   at System.ModuleHandle.ResolveTypeHandle(Int32 typeToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext)
   at System.Reflection.RuntimeModule.ResolveType(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
   at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(CustomAttributeRecord caRecord, MetadataImport scope, Assembly& lastAptcaOkAssembly, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, Object[] attributes, IList derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctor, Boolean& ctorHasParameters, Boolean& isVarArg)
   at System.Reflection.CustomAttribute.IsCustomAttributeDefined(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Int32 attributeCtorToken, Boolean mustBeInheritable)
   at System.Reflection.CustomAttribute.IsDefined(RuntimeType type, RuntimeType caType, Boolean inherit)

Is it possible someway to workaround this problem? How do I check whether XSerializationOptions is defined on SomeType without referencing a completely irrelevant YSerializationLibrary?

The problem becomes even worse once you consider that XSerializationLibrary itself calls to Enum.IsDefined; and, as such, it becomes impossible to use SomeType for serialization with XSerializationLibrary unless you also reference YSerializationLibrary.


回答1:


In your very special case of lack of access to Y library at runtime, you might try to fake the attribute with a new class with the same name and namespace of that you don't have access to:

using System;
using System.Linq;
using ClassLibraryAssembly;
using OtherAssemblyX;

// This is a faking attribute, with same signature of that in unavailable assembly.
namespace OtherAssemblyY
{
    public class YAttribute : Attribute
    {
    }
}

namespace MainAssembly
{
    class Program
    {
        static void Main(string[] args)
        {
            var type = typeof (SomeType);

            AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) =>
            {
                if (eventArgs.Name == "OtherAssemblyY, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
                    return typeof(Program).Assembly;
                return null;
            };

            Console.WriteLine(type.IsDefined(typeof (XAttribute), true));

            foreach (var attrObject in type.GetCustomAttributes(true))
            {
                Console.WriteLine("Attribute found: {0}, Assembly: {1}", attrObject, attrObject.GetType().Assembly);
            }
        }
    }
}

Result:

True
Attribute found: OtherAssemblyY.YAttribute, Assembly: MainAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Attribute found: OtherAssemblyX.XAttribute, Assembly: OtherAssemblyX, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null



回答2:


I'm not sure what you want to achieve at the end, as not loading Assemblies during run-time severely limits what you can do, because CLR Type Resolver loads dependent assemblies on type loads, issues aside, you can use Mono.Cecil to check attributes are defined without dependent assemblies being references or even present.

here is a little sample

MyXAttribute from OptionX Assembly:

 public class MyXAttribute:Attribute
    {
        public string TextX { get; set; }
    }

MyYAttribute from OptionY Assembly:

 public class MyYAttribute:Attribute
    {
        public string TextX { get; set; }
    }

MyClass from TestC Assembly: (has reference to both OptionX and OptionY assemblies)

[MyX(TextX="x")]
[MyY(TextY="y")]
class MyClass
{
}

Resolver from Main Application: (doesn't have any reference to TestC, OptionX and OptionY assemblies, and none of OptionX and OptionY are present during resolve)

    static void Main(string[] args)
    {
        var assemblyPath = @"lib\TestC.exe"; //only Testc.exe here not dependent assemblies
        var typeFullname = "TestC.MyClass";
        var attributeFullName = "OptionX.MyXAttribute";

        var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);

        var type=assembly.MainModule.Types.First(t => t.FullName == typeFullname);
        var attributes=type.CustomAttributes.Where(a => a.AttributeType.FullName == attributeFullName).ToList();

        if (attributes.Count == 0)
        {
            //type is not decorated with attribute
            return;
        }

        Console.WriteLine("Args");
        foreach (var a in attributes)
            foreach(var arg in a.ConstructorArguments)
                Console.WriteLine("{0}: {1}",arg.Type.Name,arg.Value);

        Console.WriteLine("Properties");
        foreach(var a in attributes)
            foreach(var p in a.Properties)
                Console.WriteLine("{0}: {1}",p.Name,p.Argument.Value);

    }


来源:https://stackoverflow.com/questions/25803956/how-to-prevent-memberinfo-isdefined-from-throwing-filenotfoundexception-on-irrel

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!