Activator.CreateInstance приведение к типу во время выполнения - PullRequest
0 голосов
/ 05 февраля 2019

Я пишу небольшой фреймворк, который использует много отражений, и я хочу разделить свой код на несколько проектов для лучшей поддержки.

Основной проект содержит кэш статического отражения, где я сканирую все сборкидля определенных типов и кэшируйте эти результаты для будущего использования при обработке атрибутов.

Этот кэш генерирует набор различных контекстов свойств в зависимости от того, какие типы атрибутов были найдены в классах.Т.е.

PropertyAttributeContext -> общие атрибуты, применяемые к классу / свойствам XmlPropertyAttributeContext -> специфические атрибуты xml, применяемые к классу / свойствам JsonPropertyAttributeContext -> специфичные для json атрибуты, применяемые к классу / свойствам

Эти типы не являютсяопределено в основном проекте.Что я хочу сделать, так это то, что если пользователь загружает JsonProject, этот проект автоматически зарегистрирует себя и его типы в кеше.Таким образом, при инициализации кэша загружаются все зарегистрированные типы.Все конкретные типы исходят из одного и того же абстрактного базового класса.

Я не смог найти способ сделать это с обобщениями, потому что это потребовало бы объявления и итерации по несвязанному ConcurrentBag, что невозможно.

Т.е. это не работает:

public class MetaDataEntry<I,T>
{
    public I Interface { get; set; }
    public T Type { get; set; }
}

class MetaDataRegistry
{
    // OOPS
    public static ConcurrentBag<MetaDataEntry<,>> Entries = new ConcurrentBag<MetaDataEntry<,>>(); 

    public void Register<I, T>()
    {
        Entries.Add(new MetaDataEntry<I,T>());
    }
}

Так что, похоже, я ограничен использованием typeof и Activator.CreateInstance для создания экземпляров моих типов.Проблема, с которой я сталкиваюсь, заключается в том, что у меня нет возможности преобразовать созданный объект в его производный тип.Ниже вы можете видеть, что я храню ConcurrentDictionary ключа Type и AbstractAttributeContext, из которого получен экземпляр.

using ConcurrentDictionaryTypeContext = System.Collections.Concurrent.ConcurrentDictionary<System.Type, Integration.Cache.Context.AbstractAttributeContext>;

public class ClassMetaData
{
    private ConcurrentDictionary<PropertyInfo, ConcurrentDictionaryTypeContext> _propertyContexts;
    private ClassAttributeContext _classAttributeContext;
    private Type _classType;
    private PropertyInfo[] _properties;

    public ClassMetaData(Type type)
    {
        _classType = type;
        _classAttributeContext = new ClassAttributeContext(type);
        _properties = type.GetProperties(ReflectionUtils.GetPublicBindingFlags());
        _propertyContexts = new ConcurrentDictionary<PropertyInfo, ConcurrentDictionaryTypeContext>();


        foreach (PropertyInfo property in _properties)
        {
            ConcurrentDictionaryTypeContext propertyDictionary = new ConcurrentDictionaryTypeContext();

            foreach(MetaDataEntry entry in MetaDataRegistry.Entries)
            {
                if (ReflectionUtils.HasCustomAttributeType(entry.Interface, property))
                // ****** CANNOT CAST THIS TO CONCRETE TYPE ********
                    if (!propertyDictionary.TryAdd(entry.Type, Activator.CreateInstance(entry.Type, new object[]{ property })))
                        throw new ReflectionCacheException("Type " + type.AssemblyQualifiedName + " could not be added to the cache because property " + property.Name + " could not be added to cache");

            }

            if (!_propertyContexts.TryAdd(property, propertyDictionary))
                throw new ReflectionCacheException("Type " + type.AssemblyQualifiedName + " could not be added to the cache");
        }        
    }

    public Type Type { get { return _classType; } }

    public PropertyInfo[] Properties { get { return _properties; } }

    public ClassAttributeContext ClassAttributeContext { get { return _classAttributeContext; } }

    public AttributeContextType GetAttributeContextForProperty<AttributeContextType>(PropertyInfo property) where AttributeContextType : AbstractAttributeContext
    {
        return (AttributeContextType)_propertyContexts[property][typeof(AttributeContextType)];
    }

    public bool HasPropertyAttributeContext<AttributeContextType>(PropertyInfo property)
    {
        return _propertyContexts.ContainsKey(property) && _propertyContexts[property].ContainsKey(typeof(AttributeContextType));
    }

    public ConcurrentDictionaryTypeContext GetContextsForProperty(PropertyInfo property)
    {
        return _propertyContexts[property];
    }
}

public class MetaDataCache
{
    private static ConcurrentDictionary<Type, ClassMetaData> _classes = Initialize();
    private static ConcurrentDictionary<Type, ClassMetaData> Initialize()
    {
        Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
        var keyValuePairs = assemblies.Select((Assembly assembly) =>
        {
            var qq = assembly.GetTypes()
            .Where((Type type) => 
                {
                    return type.GetProperties(ReflectionUtils.GetPublicBindingFlags())
                               .Any((PropertyInfo property) => property.GetCustomAttributes(typeof(IAttributeMarker), true).Length > 0);
                }) 
            .Select((Type type) => new KeyValuePair<Type, ClassMetaData>(type, new ClassMetaData(type)));
            return qq;
        });

        return new ConcurrentDictionary<Type, ClassMetaData>(keyValuePairs.SelectMany(i => i));
    }

    public static ClassMetaData Get<T>()
    {
        return Get(typeof(T));
    }

    public static ClassMetaData Get(Type type)
    {
        if (_classes.ContainsKey(type))
            return _classes[type];
        else
        {
            // TODO: Include search for class level attributes
            if (ReflectionUtils.GetCustomAttributesForAllProperties<IAttributeMarker>(type).Length > 0)
            {
                if (!_classes.TryAdd(type, new ClassMetaData(type)))
                    throw new ReflectionCacheException("Type " + type.AssemblyQualifiedName + " could not be added to the cache");
                else
                    return _classes[type];
            }
            else
                throw new InvalidTypeException("Type " + type.AssemblyQualifiedName + " does not have any attributes applied to the class");
        }
    }

    public static bool Contains(Type type)
    {
        return _classes.ContainsKey(type);
    }
}

public class MetaDataEntry
{
    public Type Interface { get; set; }
    public Type Type { get; set; }
}

class MetaDataRegistry
{
    public static ConcurrentBag<MetaDataEntry> Entries = new ConcurrentBag<MetaDataEntry>();

    public void Register(Type interface_, Type type)
    {
        Entries.Add(new MetaDataEntry() { Interface = interface_, Type = type });
    }

    public void Register<I, T>()
    {
        Entries.Add(new MetaDataEntry() { Interface = typeof(I), Type = typeof(T) });
    }
}

Конечно, это должно быть возможно достичь?

...