GoF Factory 的命名约定?
Naming convention for GoF Factory?
此模式使用抽象工厂,然后是工厂的实现。
我确定这两个 类 有标准的命名约定,但我不知道它是什么。
例如:
public abstract class ChocolateFactory { };
public class MyChocolateFactory { } : ChocolateFactory
这里的标准约定是什么?
我在考虑 ChocolateFactoryBase 或 ConcreteChocolateFactory,但也许还有其他东西(很像 Enums 往往以 Enum 为后缀,例如 PetTypeEnum
这样你就可以 PetTypeEnum PetType;
希望这不是主观的。
我不知道这里有什么约定,但我认为这在很大程度上取决于具体情况。我只在依赖注入场景中使用 AbstractFactory,我想在运行时创建类型并且我必须提供抽象。
- "abstract" 部分可以是一个接口 -> 惯例是 IName
- 您的基础 class 应该描述有关其实现的 "common" 事情,信息位于上下文中:如果在您的上下文中 ChocolateFactory 可能是一个 "abstract" 概念,则实现(应该描述具体的东西(MyChocolateFactory 不是一个好名字))应该显示它们的(是)关系以及具体的用例。
- 关于您的评论:如果不需要任何其他工厂实现只是为了将来可能的用例,请不要使用抽象工厂;)
你可以使用这样的东西:
// general interface for abstract factory (this is optional)
public abstract class AbstractFactory { };
// interface that uses a type of factory and produce abstract product
public abstract class AbstractChocolateFactory : AbstractFactory { };
// family of concrete factories that produce concrete products
public class NestleChocolateFactory { } : AbstractChocolateFactory
public class SwissChocolateFactory { } : AbstractChocolateFactory
这只是想法,但要使用何种抽象工厂模式实现完全取决于您的具体任务。
问题与答案
OK,这道题从一道抽象工厂的命名题开始。根据经验,始终使用 'formal' 命名您正在做的事情(例如 Factory、Decorator 等)和具体实现的描述(例如 Snickers、Mars、MotifWidget 等)。
所以基本上,您创建了一个 MSSQLConnection
,这是您描述的具体事物,Factory
,这意味着它遵循工厂模式的特征。
好的,命名和原始问题到此为止。现在是很酷的东西。讨论转向在 C# 中实现抽象工厂的最佳方式,这是一个不同的话题。我在 C# 中实现所有设计模式方面做了很多工作,为此我将在此处分享有关工厂的一些详细信息。这里是:
抽象工厂和工厂
抽象工厂基本上是基础 class 或接口与具体实现的组合。如果您共享大量代码,您需要一个基础 class,如果您不共享,则需要一个接口。
我通常会区分'factories'和'abstract factories'。工厂是创建对象(某种类型)的东西,'abstract factory' 是创建任意类型对象的东西。因此,抽象工厂的实现是工厂。这与下一条信息相关。
工厂模式
支持RTTI的语言都可以实现工厂模式。工厂模式是创建对象的东西。最简单的实现是 class,它只包含创建对象的方法,例如:
// ...
public void CreateConnection()
{
return new SqlConnection();
}
// ...
你通常用它来抽象事物。例如,HTML 解析器中生成 XML 节点的事物根据 HTML 标记创建特定类型的节点。
工厂通常根据运行时信息做出决策。因此可以概括工厂模式来实现类似的东西:
public T Create(string name)
{
// lookup constructor, invoke.
}
使用为每个名称存储 Type
的 RTTI 创建通用工厂模式非常容易。查找名称,使用反射创建对象。完成。
哦,作为奖励,与手工制作所有工厂相比,您编写的代码要少得多。因为所有的实现都是一样的,你不妨把它放在一个基class中,然后在静态构造函数中填充Dictionary。
泛化抽象工厂
抽象工厂基本上是工厂的集合,它们以与工厂模式相同的方式创建对象。唯一共享的是接口(例如,创建或者您可以使用继承来创建抽象)。
实现非常简单,所以我就此打住。
解耦工厂和类型
让我们回到 GoF 的例子。他们谈论一个 MotifFactory
和一个 PMFactory
。将来我们会遇到另一个 UI 东西,我们需要 ASPNETFactory
或 SilverlightFactory
。然而,未来是未知的,如果我们不需要的话,我们宁愿不发布我们的旧 DLL——毕竟,那是不灵活的。
如果我们想向我们的工厂添加一个新方法,就会出现第二个问题。因此,这样做将涉及更改所有工厂。您可能已经猜到了,我不想在多个地方更改它。
幸运的是我们可以同时解决这两个问题。接口是相同的(甚至可以通用化),因此我们可以在运行时简单地向我们的工厂添加新功能。
我们可以使用属性告诉 class 它应该由 由 某个工厂实现,而不是告诉工厂要创建什么对象。我们还可以在程序集加载期间扫描所有类型,因此如果加载程序集,我们可以简单地即时构建新工厂。
我为此牺牲的是编译时检查,但因为工厂模式通常使用运行时信息,所以这不一定是个问题。
总结一下,这是我工厂的代码:
/// <summary>
/// This attribute is used to tag classes, enabling them to be constructed by a Factory class. See the <see cref="Factory{Key,Intf}"/>
/// class for details.
/// </summary>
/// <remarks>
/// <para>
/// It is okay to mark classes with multiple FactoryClass attributes, even when using different keys or different factories.
/// </para>
/// </remarks>
/// <seealso cref="Factory{Key,Intf}"/>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class FactoryClassAttribute : Attribute
{
/// <summary>
/// This marks a class as eligible for construction by the specified factory type.
/// </summary>
/// <example>
/// [FactoryClass("ScrollBar", typeof(MotifFactory))]
/// public class MotifScrollBar : IControl { }
/// </example>
/// <param name="key">The key used to construct the object</param>
/// <param name="factoryType">The type of the factory class</param>
public FactoryClassAttribute(object key, Type factoryType)
{
if ((factoryType.IsGenericType &&
factoryType.GetGenericTypeDefinition() == typeof(Factory<,>)) ||
factoryType.IsAbstract ||
factoryType.IsInterface)
{
throw new NotSupportedException("Incorrect factory type: you cannot use GenericFactory or an abstract type as factory.");
}
this.Key = key;
this.FactoryType = factoryType;
}
/// <summary>
/// The key used to construct the object when calling the <see cref="Factory{Key,Intf}.Create(Key)"/> method.
/// </summary>
public object Key { get; private set; }
/// <summary>
/// The type of the factory class
/// </summary>
public Type FactoryType { get; private set; }
}
/// <summary>
/// Provides an interface for creating related or dependent objects.
/// </summary>
/// <remarks>
/// <para>
/// This class is an implementation of the Factory pattern. Your factory class should inherit this Factory class and
/// you should use the [<see cref="FactoryClassAttribute"/>] attribute on the objects that are created by the factory.
/// The implementation assumes all created objects share the same constructor signature (which is not checked by the Factory).
/// All implementations also share the same <typeparamref name="Intf"/> type and are stored by key. During runtime, you can
/// use the Factory class implementation to build objects of the correct type.
/// </para>
/// <para>
/// The Abstract Factory pattern can be implemented by adding a base Factory class with multiple factory classes that inherit from
/// the base class and are used for registration. (See below for a complete code example).
/// </para>
/// <para>
/// Implementation of the Strategy pattern can be done by using the Factory pattern and making the <typeparamref name="Intf"/>
/// implementations algorithms. When using the Strategy pattern, you still need to have some logic that picks when to use which key.
/// In some cases it can be useful to use the Factory overload with the type conversion to map keys on other keys. When implementing
/// the strategy pattern, it is possible to use this overload to determine which algorithm to use.
/// </para>
/// </remarks>
/// <typeparam name="Key">The type of the key to use for looking up the correct object type</typeparam>
/// <typeparam name="Intf">The base interface that all classes created by the Factory share</typeparam>
/// <remarks>
/// The factory class automatically hooks to all loaded assemblies by the current AppDomain. All classes tagged with the FactoryClass
/// are automatically registered.
/// </remarks>
/// <example>
/// <code lang="c#">
/// // Create the scrollbar and register it to the factory of the Motif system
/// [FactoryClass("ScrollBar", typeof(MotifFactory))]
/// public class MotifScrollBar : IControl { }
///
/// // [...] add other classes tagged with the FactoryClass attribute here...
///
/// public abstract class WidgetFactory : Factory<string, IControl>
/// {
/// public IControl CreateScrollBar() { return Create("ScrollBar") as IScrollBar; }
/// }
///
/// public class MotifFactory : WidgetFactory { }
/// public class PMFactory : WidgetFactory { }
///
/// // [...] use the factory to create a scrollbar
///
/// WidgetFactory widgetFactory = new MotifFactory();
/// var scrollbar = widgetFactory.CreateScrollBar(); // this is a MotifScrollbar intance
/// </code>
/// </example>
public abstract class Factory<Key, Intf> : IFactory<Key, Intf>
where Intf : class
{
/// <summary>
/// Creates a factory by mapping the keys of the create method to the keys in the FactoryClass attributes.
/// </summary>
protected Factory() : this((a) => (a)) { }
/// <summary>
/// Creates a factory by using a custom mapping function that defines the mapping of keys from the Create
/// method, to the keys in the FactoryClass attributes.
/// </summary>
/// <param name="typeConversion">A function that maps keys passed to <see cref="Create(Key)"/> to keys used with [<see cref="FactoryClassAttribute"/>]</param>
protected Factory(Func<Key, object> typeConversion)
{
this.typeConversion = typeConversion;
}
private Func<Key, object> typeConversion;
private static object lockObject = new object();
private static Dictionary<Type, Dictionary<object, Type>> dict = null;
/// <summary>
/// Creates an instance a class registered with the <see cref="FactoryClassAttribute"/> attribute by looking up the key.
/// </summary>
/// <param name="key">The key used to lookup the attribute. The key is first converted using the typeConversion function passed
/// to the constructor if this was defined.</param>
/// <returns>An instance of the factory class</returns>
public virtual Intf Create(Key key)
{
Dictionary<Type, Dictionary<object, Type>> dict = Init();
Dictionary<object, Type> factoryDict;
if (dict.TryGetValue(this.GetType(), out factoryDict))
{
Type t;
return (factoryDict.TryGetValue(typeConversion(key), out t)) ? (Intf)Activator.CreateInstance(t) : null;
}
return null;
}
/// <summary>
/// Creates an instance a class registered with the <see cref="FactoryClassAttribute"/> attribute by looking up the key.
/// </summary>
/// <param name="key">The key used to lookup the attribute. The key is first converted using the typeConversion function passed
/// to the constructor if this was defined.</param>
/// <param name="constructorParameters">Additional parameters that have to be passed to the constructor</param>
/// <returns>An instance of the factory class</returns>
public virtual Intf Create(Key key, params object[] constructorParameters)
{
Dictionary<Type, Dictionary<object, Type>> dict = Init();
Dictionary<object, Type> factoryDict;
if (dict.TryGetValue(this.GetType(), out factoryDict))
{
Type t;
return (factoryDict.TryGetValue(typeConversion(key), out t)) ? (Intf)Activator.CreateInstance(t, constructorParameters) : null;
}
return null;
}
/// <summary>
/// Enumerates all registered attribute keys. No transformation is done here.
/// </summary>
/// <returns>All keys currently known to this factory</returns>
public virtual IEnumerable<Key> EnumerateKeys()
{
Dictionary<Type, Dictionary<object, Type>> dict = Init();
Dictionary<object, Type> factoryDict;
if (dict.TryGetValue(this.GetType(), out factoryDict))
{
foreach (object key in factoryDict.Keys)
{
yield return (Key)key;
}
}
}
private void TryHook()
{
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(NewAssemblyLoaded);
}
private Dictionary<Type, Dictionary<object, Type>> Init()
{
Dictionary<Type, Dictionary<object, Type>> d = dict;
if (d == null)
{
lock (lockObject)
{
if (dict == null)
{
try
{
TryHook();
}
catch (Exception) { } // Not available in this security mode. You're probably using shared hosting
ScanTypes();
}
d = dict;
}
}
return d;
}
private void ScanTypes()
{
Dictionary<Type, Dictionary<object, Type>> classDict = new Dictionary<Type, Dictionary<object, Type>>();
foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
{
AddAssemblyTypes(classDict, ass);
}
dict = classDict;
}
private void AddAssemblyTypes(Dictionary<Type, Dictionary<object, Type>> classDict, Assembly ass)
{
try
{
foreach (Type t in ass.GetTypes())
{
if (t.IsClass && !t.IsAbstract &&
typeof(Intf).IsAssignableFrom(t))
{
object[] fca = t.GetCustomAttributes(typeof(FactoryClassAttribute), false);
foreach (FactoryClassAttribute f in fca)
{
if (!(f.Key is Key))
{
throw new InvalidCastException(string.Format("Cannot cast key of factory object {0} to {1}", t.FullName, typeof(Key).FullName));
}
Dictionary<object, Type> keyDict;
if (!classDict.TryGetValue(f.FactoryType, out keyDict))
{
keyDict = new Dictionary<object, Type>();
classDict.Add(f.FactoryType, keyDict);
}
keyDict.Add(f.Key, t);
}
}
}
}
catch (ReflectionTypeLoadException) { } // An assembly we cannot process. That also means we cannot use it.
}
private void NewAssemblyLoaded(object sender, AssemblyLoadEventArgs args)
{
lock (lockObject)
{
// Make sure new 'create' invokes wait till we're done updating the factory
Dictionary<Type, Dictionary<object, Type>> classDict = new Dictionary<Type, Dictionary<object, Type>>(dict);
dict = null;
Thread.MemoryBarrier();
AddAssemblyTypes(classDict, args.LoadedAssembly);
dict = classDict;
}
}
}
此模式使用抽象工厂,然后是工厂的实现。
我确定这两个 类 有标准的命名约定,但我不知道它是什么。
例如:
public abstract class ChocolateFactory { };
public class MyChocolateFactory { } : ChocolateFactory
这里的标准约定是什么?
我在考虑 ChocolateFactoryBase 或 ConcreteChocolateFactory,但也许还有其他东西(很像 Enums 往往以 Enum 为后缀,例如 PetTypeEnum
这样你就可以 PetTypeEnum PetType;
希望这不是主观的。
我不知道这里有什么约定,但我认为这在很大程度上取决于具体情况。我只在依赖注入场景中使用 AbstractFactory,我想在运行时创建类型并且我必须提供抽象。
- "abstract" 部分可以是一个接口 -> 惯例是 IName
- 您的基础 class 应该描述有关其实现的 "common" 事情,信息位于上下文中:如果在您的上下文中 ChocolateFactory 可能是一个 "abstract" 概念,则实现(应该描述具体的东西(MyChocolateFactory 不是一个好名字))应该显示它们的(是)关系以及具体的用例。
- 关于您的评论:如果不需要任何其他工厂实现只是为了将来可能的用例,请不要使用抽象工厂;)
你可以使用这样的东西:
// general interface for abstract factory (this is optional)
public abstract class AbstractFactory { };
// interface that uses a type of factory and produce abstract product
public abstract class AbstractChocolateFactory : AbstractFactory { };
// family of concrete factories that produce concrete products
public class NestleChocolateFactory { } : AbstractChocolateFactory
public class SwissChocolateFactory { } : AbstractChocolateFactory
这只是想法,但要使用何种抽象工厂模式实现完全取决于您的具体任务。
问题与答案
OK,这道题从一道抽象工厂的命名题开始。根据经验,始终使用 'formal' 命名您正在做的事情(例如 Factory、Decorator 等)和具体实现的描述(例如 Snickers、Mars、MotifWidget 等)。
所以基本上,您创建了一个 MSSQLConnection
,这是您描述的具体事物,Factory
,这意味着它遵循工厂模式的特征。
好的,命名和原始问题到此为止。现在是很酷的东西。讨论转向在 C# 中实现抽象工厂的最佳方式,这是一个不同的话题。我在 C# 中实现所有设计模式方面做了很多工作,为此我将在此处分享有关工厂的一些详细信息。这里是:
抽象工厂和工厂
抽象工厂基本上是基础 class 或接口与具体实现的组合。如果您共享大量代码,您需要一个基础 class,如果您不共享,则需要一个接口。
我通常会区分'factories'和'abstract factories'。工厂是创建对象(某种类型)的东西,'abstract factory' 是创建任意类型对象的东西。因此,抽象工厂的实现是工厂。这与下一条信息相关。
工厂模式
支持RTTI的语言都可以实现工厂模式。工厂模式是创建对象的东西。最简单的实现是 class,它只包含创建对象的方法,例如:
// ...
public void CreateConnection()
{
return new SqlConnection();
}
// ...
你通常用它来抽象事物。例如,HTML 解析器中生成 XML 节点的事物根据 HTML 标记创建特定类型的节点。
工厂通常根据运行时信息做出决策。因此可以概括工厂模式来实现类似的东西:
public T Create(string name)
{
// lookup constructor, invoke.
}
使用为每个名称存储 Type
的 RTTI 创建通用工厂模式非常容易。查找名称,使用反射创建对象。完成。
哦,作为奖励,与手工制作所有工厂相比,您编写的代码要少得多。因为所有的实现都是一样的,你不妨把它放在一个基class中,然后在静态构造函数中填充Dictionary。
泛化抽象工厂
抽象工厂基本上是工厂的集合,它们以与工厂模式相同的方式创建对象。唯一共享的是接口(例如,创建或者您可以使用继承来创建抽象)。
实现非常简单,所以我就此打住。
解耦工厂和类型
让我们回到 GoF 的例子。他们谈论一个 MotifFactory
和一个 PMFactory
。将来我们会遇到另一个 UI 东西,我们需要 ASPNETFactory
或 SilverlightFactory
。然而,未来是未知的,如果我们不需要的话,我们宁愿不发布我们的旧 DLL——毕竟,那是不灵活的。
如果我们想向我们的工厂添加一个新方法,就会出现第二个问题。因此,这样做将涉及更改所有工厂。您可能已经猜到了,我不想在多个地方更改它。
幸运的是我们可以同时解决这两个问题。接口是相同的(甚至可以通用化),因此我们可以在运行时简单地向我们的工厂添加新功能。
我们可以使用属性告诉 class 它应该由 由 某个工厂实现,而不是告诉工厂要创建什么对象。我们还可以在程序集加载期间扫描所有类型,因此如果加载程序集,我们可以简单地即时构建新工厂。
我为此牺牲的是编译时检查,但因为工厂模式通常使用运行时信息,所以这不一定是个问题。
总结一下,这是我工厂的代码:
/// <summary>
/// This attribute is used to tag classes, enabling them to be constructed by a Factory class. See the <see cref="Factory{Key,Intf}"/>
/// class for details.
/// </summary>
/// <remarks>
/// <para>
/// It is okay to mark classes with multiple FactoryClass attributes, even when using different keys or different factories.
/// </para>
/// </remarks>
/// <seealso cref="Factory{Key,Intf}"/>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class FactoryClassAttribute : Attribute
{
/// <summary>
/// This marks a class as eligible for construction by the specified factory type.
/// </summary>
/// <example>
/// [FactoryClass("ScrollBar", typeof(MotifFactory))]
/// public class MotifScrollBar : IControl { }
/// </example>
/// <param name="key">The key used to construct the object</param>
/// <param name="factoryType">The type of the factory class</param>
public FactoryClassAttribute(object key, Type factoryType)
{
if ((factoryType.IsGenericType &&
factoryType.GetGenericTypeDefinition() == typeof(Factory<,>)) ||
factoryType.IsAbstract ||
factoryType.IsInterface)
{
throw new NotSupportedException("Incorrect factory type: you cannot use GenericFactory or an abstract type as factory.");
}
this.Key = key;
this.FactoryType = factoryType;
}
/// <summary>
/// The key used to construct the object when calling the <see cref="Factory{Key,Intf}.Create(Key)"/> method.
/// </summary>
public object Key { get; private set; }
/// <summary>
/// The type of the factory class
/// </summary>
public Type FactoryType { get; private set; }
}
/// <summary>
/// Provides an interface for creating related or dependent objects.
/// </summary>
/// <remarks>
/// <para>
/// This class is an implementation of the Factory pattern. Your factory class should inherit this Factory class and
/// you should use the [<see cref="FactoryClassAttribute"/>] attribute on the objects that are created by the factory.
/// The implementation assumes all created objects share the same constructor signature (which is not checked by the Factory).
/// All implementations also share the same <typeparamref name="Intf"/> type and are stored by key. During runtime, you can
/// use the Factory class implementation to build objects of the correct type.
/// </para>
/// <para>
/// The Abstract Factory pattern can be implemented by adding a base Factory class with multiple factory classes that inherit from
/// the base class and are used for registration. (See below for a complete code example).
/// </para>
/// <para>
/// Implementation of the Strategy pattern can be done by using the Factory pattern and making the <typeparamref name="Intf"/>
/// implementations algorithms. When using the Strategy pattern, you still need to have some logic that picks when to use which key.
/// In some cases it can be useful to use the Factory overload with the type conversion to map keys on other keys. When implementing
/// the strategy pattern, it is possible to use this overload to determine which algorithm to use.
/// </para>
/// </remarks>
/// <typeparam name="Key">The type of the key to use for looking up the correct object type</typeparam>
/// <typeparam name="Intf">The base interface that all classes created by the Factory share</typeparam>
/// <remarks>
/// The factory class automatically hooks to all loaded assemblies by the current AppDomain. All classes tagged with the FactoryClass
/// are automatically registered.
/// </remarks>
/// <example>
/// <code lang="c#">
/// // Create the scrollbar and register it to the factory of the Motif system
/// [FactoryClass("ScrollBar", typeof(MotifFactory))]
/// public class MotifScrollBar : IControl { }
///
/// // [...] add other classes tagged with the FactoryClass attribute here...
///
/// public abstract class WidgetFactory : Factory<string, IControl>
/// {
/// public IControl CreateScrollBar() { return Create("ScrollBar") as IScrollBar; }
/// }
///
/// public class MotifFactory : WidgetFactory { }
/// public class PMFactory : WidgetFactory { }
///
/// // [...] use the factory to create a scrollbar
///
/// WidgetFactory widgetFactory = new MotifFactory();
/// var scrollbar = widgetFactory.CreateScrollBar(); // this is a MotifScrollbar intance
/// </code>
/// </example>
public abstract class Factory<Key, Intf> : IFactory<Key, Intf>
where Intf : class
{
/// <summary>
/// Creates a factory by mapping the keys of the create method to the keys in the FactoryClass attributes.
/// </summary>
protected Factory() : this((a) => (a)) { }
/// <summary>
/// Creates a factory by using a custom mapping function that defines the mapping of keys from the Create
/// method, to the keys in the FactoryClass attributes.
/// </summary>
/// <param name="typeConversion">A function that maps keys passed to <see cref="Create(Key)"/> to keys used with [<see cref="FactoryClassAttribute"/>]</param>
protected Factory(Func<Key, object> typeConversion)
{
this.typeConversion = typeConversion;
}
private Func<Key, object> typeConversion;
private static object lockObject = new object();
private static Dictionary<Type, Dictionary<object, Type>> dict = null;
/// <summary>
/// Creates an instance a class registered with the <see cref="FactoryClassAttribute"/> attribute by looking up the key.
/// </summary>
/// <param name="key">The key used to lookup the attribute. The key is first converted using the typeConversion function passed
/// to the constructor if this was defined.</param>
/// <returns>An instance of the factory class</returns>
public virtual Intf Create(Key key)
{
Dictionary<Type, Dictionary<object, Type>> dict = Init();
Dictionary<object, Type> factoryDict;
if (dict.TryGetValue(this.GetType(), out factoryDict))
{
Type t;
return (factoryDict.TryGetValue(typeConversion(key), out t)) ? (Intf)Activator.CreateInstance(t) : null;
}
return null;
}
/// <summary>
/// Creates an instance a class registered with the <see cref="FactoryClassAttribute"/> attribute by looking up the key.
/// </summary>
/// <param name="key">The key used to lookup the attribute. The key is first converted using the typeConversion function passed
/// to the constructor if this was defined.</param>
/// <param name="constructorParameters">Additional parameters that have to be passed to the constructor</param>
/// <returns>An instance of the factory class</returns>
public virtual Intf Create(Key key, params object[] constructorParameters)
{
Dictionary<Type, Dictionary<object, Type>> dict = Init();
Dictionary<object, Type> factoryDict;
if (dict.TryGetValue(this.GetType(), out factoryDict))
{
Type t;
return (factoryDict.TryGetValue(typeConversion(key), out t)) ? (Intf)Activator.CreateInstance(t, constructorParameters) : null;
}
return null;
}
/// <summary>
/// Enumerates all registered attribute keys. No transformation is done here.
/// </summary>
/// <returns>All keys currently known to this factory</returns>
public virtual IEnumerable<Key> EnumerateKeys()
{
Dictionary<Type, Dictionary<object, Type>> dict = Init();
Dictionary<object, Type> factoryDict;
if (dict.TryGetValue(this.GetType(), out factoryDict))
{
foreach (object key in factoryDict.Keys)
{
yield return (Key)key;
}
}
}
private void TryHook()
{
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(NewAssemblyLoaded);
}
private Dictionary<Type, Dictionary<object, Type>> Init()
{
Dictionary<Type, Dictionary<object, Type>> d = dict;
if (d == null)
{
lock (lockObject)
{
if (dict == null)
{
try
{
TryHook();
}
catch (Exception) { } // Not available in this security mode. You're probably using shared hosting
ScanTypes();
}
d = dict;
}
}
return d;
}
private void ScanTypes()
{
Dictionary<Type, Dictionary<object, Type>> classDict = new Dictionary<Type, Dictionary<object, Type>>();
foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
{
AddAssemblyTypes(classDict, ass);
}
dict = classDict;
}
private void AddAssemblyTypes(Dictionary<Type, Dictionary<object, Type>> classDict, Assembly ass)
{
try
{
foreach (Type t in ass.GetTypes())
{
if (t.IsClass && !t.IsAbstract &&
typeof(Intf).IsAssignableFrom(t))
{
object[] fca = t.GetCustomAttributes(typeof(FactoryClassAttribute), false);
foreach (FactoryClassAttribute f in fca)
{
if (!(f.Key is Key))
{
throw new InvalidCastException(string.Format("Cannot cast key of factory object {0} to {1}", t.FullName, typeof(Key).FullName));
}
Dictionary<object, Type> keyDict;
if (!classDict.TryGetValue(f.FactoryType, out keyDict))
{
keyDict = new Dictionary<object, Type>();
classDict.Add(f.FactoryType, keyDict);
}
keyDict.Add(f.Key, t);
}
}
}
}
catch (ReflectionTypeLoadException) { } // An assembly we cannot process. That also means we cannot use it.
}
private void NewAssemblyLoaded(object sender, AssemblyLoadEventArgs args)
{
lock (lockObject)
{
// Make sure new 'create' invokes wait till we're done updating the factory
Dictionary<Type, Dictionary<object, Type>> classDict = new Dictionary<Type, Dictionary<object, Type>>(dict);
dict = null;
Thread.MemoryBarrier();
AddAssemblyTypes(classDict, args.LoadedAssembly);
dict = classDict;
}
}
}