问题
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