|
using System;
using System.Reflection;
using Pfz.Extensions;
using System.Collections.Generic;
using System.Diagnostics;
namespace Pfz.Factoring
{
/// <summary>
/// Class that deals with the creation of the appropriate factory for the given factoryResultType (or base factory type).
/// </summary>
public static class Factories
{
/// <summary>
/// Creates a new factory prepared to create instances of the given factoryResultType.
/// </summary>
public static IFactories Get(Type factoryResultType)
{
if (factoryResultType == null)
throw new ArgumentNullException("factoryResultType");
Type type = typeof(Factories<>).MakeGenericType(factoryResultType);
var field = type.GetField("Instance");
object result = field.GetValue(null);
return (IFactories)result;
}
}
/// <summary>
/// Class that manages the factories. In it, you can register the types that will be returned for given data-types, or create specialized
/// factories for datat-types.
/// </summary>
/// <typeparam name="T">The type of the objects that will be created by this factory.</typeparam>
public static class Factories<T>
{
#region Instance
/// <summary>
/// Gets an Instance for this Factory. Useful only if you want to use this by interfaces.
/// </summary>
public static IFactories<T> Instance = new FactoryInstance();
private sealed class FactoryInstance:
IFactories<T>
{
public IFactory<T> TryCreateFactory(Type dataType)
{
return Factories<T>.TryCreateFactory(dataType);
}
public IFactory<T> CreateFactory(Type dataType)
{
return Factories<T>.CreateFactory(dataType);
}
public IFactory<T> TryCreateFactory<DataType>()
{
return Factories<T>.TryCreateFactory<DataType>();
}
public IFactory<T> CreateFactory<DataType>()
{
return Factories<T>.CreateFactory<DataType>();
}
IFactory IFactories.TryCreateFactory(Type dataType)
{
return Factories<T>.TryCreateFactory(dataType);
}
IFactory IFactories.CreateFactory(Type dataType)
{
return Factories<T>.CreateFactory(dataType);
}
IFactory IFactories.TryCreateFactory<DataType>()
{
return Factories<T>.TryCreateFactory<DataType>();
}
IFactory IFactories.CreateFactory<DataType>()
{
return Factories<T>.CreateFactory<DataType>();
}
}
#endregion
private static TypeDictionary<KeyValuePair<Type, ConstructorInfo>> _dictionary = new TypeDictionary<KeyValuePair<Type, ConstructorInfo>>();
static Factories()
{
if (!typeof(T).ContainsCustomAttribute<FactoryBaseAttribute>())
throw new ArgumentException("Factories<T> can only accept [FactoryBase] interfaces as generic argument.", typeof(T).FullName);
#if WINDOWS_PHONE
#else
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly != null)
{
var referencedAssemblies = entryAssembly.GetReferencedAssemblies();
foreach(var referencedAssembly in referencedAssemblies)
Assembly.Load(referencedAssembly);
}
#endif
foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach(var type in assembly.GetTypes())
{
var attributes = type.GetCustomAttributes<AutoRegisterInFactoryAttributeBase>();
foreach(var attribute in attributes)
if (attribute.BaseFactoryType == typeof(T))
Register(type, attribute.DataType, attribute.CanBeUsedForSubDataTypes);
}
}
}
/// <summary>
/// Registers a type to be created for the given datatype.
/// </summary>
public static void Register(Type typeToCreate, Type dataType, bool canCreateForSubDataTypes=false)
{
if (typeToCreate == null)
throw new ArgumentNullException("typeToCreate");
if (dataType == null)
throw new ArgumentNullException("dataType");
if (!typeof(T).IsAssignableFrom(typeToCreate))
throw new ArgumentException(typeToCreate.FullName + " must implement " + typeof(T).FullName + ".", "typeToCreate");
var constructor = typeToCreate.GetConstructor(Type.EmptyTypes);
if (constructor == null)
throw new ArgumentException(typeToCreate.FullName + " does not have a public default constructor.");
var pair = new KeyValuePair<Type, ConstructorInfo>(dataType, constructor);
_dictionary.Set(dataType, pair, canCreateForSubDataTypes);
}
/// <summary>
/// Tries to register a factory for the given dataType.
/// This will not unregister editors for parent dataTypes capable of editing sub-types.
/// </summary>
public static bool Unregister(Type dataType)
{
return _dictionary.Remove(dataType);
}
/// <summary>
/// Gets a value indicating if a factory for the given dataType can be created.
/// </summary>
public static bool CanCreate(Type dataType)
{
var constructorPair = _dictionary.FindUpOrDefault(dataType);
return constructorPair.Value != null;
}
/// <summary>
/// Tries to create a factory for the given dataType.
/// </summary>
public static Factory<T, DataType> TryCreateFactory<DataType>()
{
var constructorPair = _dictionary.FindUpOrDefault(typeof(DataType));
var constructor = constructorPair.Value;
if (constructor == null)
return null;
var realType = constructor.DeclaringType;
if (realType.ContainsGenericParameters)
realType = realType.MakeGenericType(typeof(DataType));
var typedConstructor = ReflectionHelper.GetDefaultConstructorDelegate<T>(realType);
return new Factory<T, DataType>(typedConstructor, constructorPair.Key);
}
private static readonly MethodInfo _method = ReflectionHelper.GetMethod(() => TryCreateFactory<int>()).GetGenericMethodDefinition();
/// <summary>
/// Tries to create a factory for the given dataType.
/// </summary>
public static IFactory<T> TryCreateFactory(Type dataType)
{
if (dataType == null)
throw new ArgumentNullException("dataType");
var method = _method.MakeGenericMethod(dataType);
var fastMethod = method.GetDelegate();
object result = fastMethod(null, null);
return (IFactory<T>)result;
}
/// <summary>
/// Create a factory for the given dataType or throws an exception.
/// </summary>
public static Factory<T, DataType> CreateFactory<DataType>()
{
var result = TryCreateFactory<DataType>();
if (result == null)
throw new ArgumentException("Can't find a factory for the given data-type.");
return result;
}
/// <summary>
/// Create a factory for the given dataType or throws an exception.
/// </summary>
public static IFactory<T> CreateFactory(Type dataType)
{
var result = TryCreateFactory(dataType);
if (result == null)
throw new ArgumentException("Can't find a factory for the given data-type.");
return result;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.
Want more info or simply want to contact me?
Take a look at:
http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com
Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).