I'm writing a small framework that uses a lot of reflection and I want to separate my code out into multiple projects for better maintainability.
The main project contains a static reflection cache where I scan all assemblies for certain types and cache these results for future use when processing attributes.
This cache generates a bunch of different property contexts based on what types of attributes were found on classes. Ie.
PropertyAttributeContext -> common attributes applied to class/properties XmlPropertyAttributeContext -> xml specific attributes applied to class/properties JsonPropertyAttributeContext -> json specific attributes applied to class/properties
These types are not defined in the main project. What I want to do is that if a user loads JsonProject, that project will automatically register itself and it's types to the cache. This way when the cache is being initialized all the registered types are loaded. The concrete types all extend from the same abstract base class.
I could not find a way to do this with generics, because it would require declaring and iterating over an unbound ConcurrentBag which is not possible.
Ie this doesn't work:
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>());
}
}
So it looks like I'm restricted to using typeof and Activator.CreateInstance to instantiate my types. The problem I am running into is I have no way of casting the created object into it's derived type. Below you can see I am storing a ConcurrentDictionary of key Type, and AbstractAttributeContext which the instance is derived from.
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) });
}
}
Surely this must be possible to achieve?
Aucun commentaire:
Enregistrer un commentaire