2

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.

penartur
  • 9,792
  • 5
  • 39
  • 50
  • 2
    Without loading a type how'll you check if it is of particular type? Question doesn't makes sense for me. Your code may work if you alter the order of attributes applied(am not sure, guessing here). But trying to run a program without the referenced dll is not going to work anyway. – Sriram Sakthivel Sep 12 '14 at 08:34
  • @SriramSakthivel Are you speaking of theory? In theory, I could at least say that it is definitely not of particular type if it is defined in the different assembly; you don't even need to check the type name for that. I'm not going to run a program without dlls it actually uses; yet `IsDefined` requires me to reference an assembly just because there is some other attribute my program will never use and couldn't care less about. – penartur Sep 12 '14 at 08:39
  • I don't understand what do you mean. Could you explain how can you say it if not of particular type without even need to check the type name? It sounds like a [XYProblem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) for me. Can you explain what problem you're trying to solve? – Sriram Sakthivel Sep 12 '14 at 08:42
  • @SriramSakthivel Please check out the updated question. Not XYProblem though, IMHO. – penartur Sep 12 '14 at 08:46
  • Just looked into source, It seems it gets all the attributes of given type (by loading all attibutes and checking the type) then check the count against `0`. They could have implemented short circuit, but unfortunately it is not. So you're out of luck :( – Sriram Sakthivel Sep 12 '14 at 09:17
  • @SriramSakthivel Even short-circuit would not solve the problem (what if XSerializationOptions are not defined on SomeType?) They could skip the attributes from another assemblies though. – penartur Sep 12 '14 at 09:37

2 Answers2

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
George Polevoy
  • 7,450
  • 3
  • 36
  • 61
1

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);

    }
user3473830
  • 7,165
  • 5
  • 36
  • 52