Pool can now be used with types not derived from IRecyclable and/or without a public default constructor; consolidated type-related helper methods into a common helper class (TypeHelper.cs); optimized GetFieldInfosIncludingBaseClasses() method

git-svn-id: file:///srv/devel/repo-conversion/nusu@268 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2012-03-08 11:05:20 +00:00
parent 24439da822
commit c91a082e84
18 changed files with 277 additions and 292 deletions

View File

@ -58,10 +58,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Source\Cloning\CloneFactoryTest.cs" /> <Compile Include="Source\Cloning\CloneFactoryTest.cs" />
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
<DependentUpon>ClonerHelpers.cs</DependentUpon>
</Compile>
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" /> <Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.FieldBased.cs"> <Compile Include="Source\Cloning\ExpressionTreeCloner.FieldBased.cs">
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon> <DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
@ -253,10 +249,6 @@
<Compile Include="Source\Plugins\NoPluginAttribute.Test.cs"> <Compile Include="Source\Plugins\NoPluginAttribute.Test.cs">
<DependentUpon>NoPluginAttribute.cs</DependentUpon> <DependentUpon>NoPluginAttribute.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Plugins\PluginHelper.cs" />
<Compile Include="Source\Plugins\PluginHelper.Test.cs">
<DependentUpon>PluginHelper.cs</DependentUpon>
</Compile>
<Compile Include="Source\Plugins\PluginHost.cs" /> <Compile Include="Source\Plugins\PluginHost.cs" />
<Compile Include="Source\Plugins\PluginHost.Test.cs"> <Compile Include="Source\Plugins\PluginHost.Test.cs">
<DependentUpon>PluginHost.cs</DependentUpon> <DependentUpon>PluginHost.cs</DependentUpon>
@ -285,6 +277,10 @@
<Compile Include="Source\ObservableHelper.Test.cs"> <Compile Include="Source\ObservableHelper.Test.cs">
<DependentUpon>ObservableHelper.cs</DependentUpon> <DependentUpon>ObservableHelper.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\TypeHelper.cs" />
<Compile Include="Source\TypeHelper.Test.cs">
<DependentUpon>\TypeHelper.cs</DependentUpon>
</Compile>
<Compile Include="Source\Semaphore.cs" /> <Compile Include="Source\Semaphore.cs" />
<Compile Include="Source\Semaphore.Test.cs"> <Compile Include="Source\Semaphore.Test.cs">
<DependentUpon>Semaphore.cs</DependentUpon> <DependentUpon>Semaphore.cs</DependentUpon>

View File

@ -89,10 +89,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Source\Cloning\CloneFactoryTest.cs" /> <Compile Include="Source\Cloning\CloneFactoryTest.cs" />
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
<DependentUpon>ClonerHelpers.cs</DependentUpon>
</Compile>
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" /> <Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.FieldBased.cs"> <Compile Include="Source\Cloning\ExpressionTreeCloner.FieldBased.cs">
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon> <DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
@ -284,10 +280,6 @@
<Compile Include="Source\Plugins\NoPluginAttribute.Test.cs"> <Compile Include="Source\Plugins\NoPluginAttribute.Test.cs">
<DependentUpon>NoPluginAttribute.cs</DependentUpon> <DependentUpon>NoPluginAttribute.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Plugins\PluginHelper.cs" />
<Compile Include="Source\Plugins\PluginHelper.Test.cs">
<DependentUpon>PluginHelper.cs</DependentUpon>
</Compile>
<Compile Include="Source\Plugins\PluginHost.cs" /> <Compile Include="Source\Plugins\PluginHost.cs" />
<Compile Include="Source\Plugins\PluginHost.Test.cs"> <Compile Include="Source\Plugins\PluginHost.Test.cs">
<DependentUpon>PluginHost.cs</DependentUpon> <DependentUpon>PluginHost.cs</DependentUpon>
@ -316,6 +308,10 @@
<Compile Include="Source\ObservableHelper.Test.cs"> <Compile Include="Source\ObservableHelper.Test.cs">
<DependentUpon>ObservableHelper.cs</DependentUpon> <DependentUpon>ObservableHelper.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\TypeHelper.cs" />
<Compile Include="Source\TypeHelper.Test.cs">
<DependentUpon>\TypeHelper.cs</DependentUpon>
</Compile>
<Compile Include="Source\Semaphore.cs" /> <Compile Include="Source\Semaphore.cs" />
<Compile Include="Source\Semaphore.Test.cs"> <Compile Include="Source\Semaphore.Test.cs">
<DependentUpon>Semaphore.cs</DependentUpon> <DependentUpon>Semaphore.cs</DependentUpon>

View File

@ -100,10 +100,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Source\Cloning\CloneFactoryTest.cs" /> <Compile Include="Source\Cloning\CloneFactoryTest.cs" />
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
<DependentUpon>ClonerHelpers.cs</DependentUpon>
</Compile>
<Compile Include="Source\Cloning\ExpressionTreeCloner.cs" /> <Compile Include="Source\Cloning\ExpressionTreeCloner.cs" />
<Compile Include="Source\Cloning\ExpressionTreeCloner.FieldBased.cs"> <Compile Include="Source\Cloning\ExpressionTreeCloner.FieldBased.cs">
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon> <DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
@ -295,10 +291,6 @@
<Compile Include="Source\Plugins\NoPluginAttribute.Test.cs"> <Compile Include="Source\Plugins\NoPluginAttribute.Test.cs">
<DependentUpon>NoPluginAttribute.cs</DependentUpon> <DependentUpon>NoPluginAttribute.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Plugins\PluginHelper.cs" />
<Compile Include="Source\Plugins\PluginHelper.Test.cs">
<DependentUpon>PluginHelper.cs</DependentUpon>
</Compile>
<Compile Include="Source\Plugins\PluginHost.cs" /> <Compile Include="Source\Plugins\PluginHost.cs" />
<Compile Include="Source\Plugins\PluginHost.Test.cs"> <Compile Include="Source\Plugins\PluginHost.Test.cs">
<DependentUpon>PluginHost.cs</DependentUpon> <DependentUpon>PluginHost.cs</DependentUpon>
@ -327,6 +319,10 @@
<Compile Include="Source\ObservableHelper.Test.cs"> <Compile Include="Source\ObservableHelper.Test.cs">
<DependentUpon>ObservableHelper.cs</DependentUpon> <DependentUpon>ObservableHelper.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\TypeHelper.cs" />
<Compile Include="Source\TypeHelper.Test.cs">
<DependentUpon>\TypeHelper.cs</DependentUpon>
</Compile>
<Compile Include="Source\Semaphore.cs" /> <Compile Include="Source\Semaphore.cs" />
<Compile Include="Source\Semaphore.Test.cs"> <Compile Include="Source\Semaphore.Test.cs">
<DependentUpon>Semaphore.cs</DependentUpon> <DependentUpon>Semaphore.cs</DependentUpon>

View File

@ -1,81 +0,0 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2012 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Nuclex.Support.Cloning {
/// <summary>Contains helper methods for the cloners</summary>
internal static class ClonerHelpers {
/// <summary>
/// Returns all the fields of a type, working around a weird reflection issue
/// where explicitly declared fields in base classes are returned, but not
/// automatic property backing fields.
/// </summary>
/// <param name="type">Type whose fields will be returned</param>
/// <param name="bindingFlags">Binding flags to use when querying the fields</param>
/// <returns>All of the type's fields, including its base types</returns>
public static FieldInfo[] GetFieldInfosIncludingBaseClasses(
Type type, BindingFlags bindingFlags
) {
FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
// If this class doesn't have a base, don't waste any time
if(type.BaseType == typeof(object)) {
return fieldInfos;
} else { // Otherwise, collect all types up to the furthest base class
var fieldInfoList = new List<FieldInfo>(fieldInfos);
while(type.BaseType != typeof(object)) {
type = type.BaseType;
fieldInfos = type.GetFields(bindingFlags);
// Look for fields we do not have listed yet and merge them into the main list
for(int index = 0; index < fieldInfos.Length; ++index) {
bool found = false;
for(int searchIndex = 0; searchIndex < fieldInfoList.Count; ++searchIndex) {
bool match =
(fieldInfoList[searchIndex].DeclaringType == fieldInfos[index].DeclaringType) &&
(fieldInfoList[searchIndex].Name == fieldInfos[index].Name);
if(match) {
found = true;
break;
}
}
if(!found) {
fieldInfoList.Add(fieldInfos[index]);
}
}
}
return fieldInfoList.ToArray();
}
}
}
} // namespace Nuclex.Support.Cloning

View File

@ -185,8 +185,8 @@ namespace Nuclex.Support.Cloning {
); );
// Enumerate all of the type's fields and generate transfer expressions for each // Enumerate all of the type's fields and generate transfer expressions for each
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( FieldInfo[] fieldInfos = clonedType.GetFieldInfosIncludingBaseClasses(
clonedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
); );
for(int index = 0; index < fieldInfos.Length; ++index) { for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index]; FieldInfo fieldInfo = fieldInfos[index];
@ -495,8 +495,8 @@ namespace Nuclex.Support.Cloning {
ICollection<Expression> transferExpressions ICollection<Expression> transferExpressions
) { ) {
// Enumerate all of the type's fields and generate transfer expressions for each // Enumerate all of the type's fields and generate transfer expressions for each
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( FieldInfo[] fieldInfos = clonedType.GetFieldInfosIncludingBaseClasses(
clonedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
); );
for(int index = 0; index < fieldInfos.Length; ++index) { for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index]; FieldInfo fieldInfo = fieldInfos[index];

View File

@ -167,8 +167,8 @@ namespace Nuclex.Support.Cloning {
object clone = FormatterServices.GetUninitializedObject(originalType); object clone = FormatterServices.GetUninitializedObject(originalType);
#endif #endif
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
); );
for(int index = 0; index < fieldInfos.Length; ++index) { for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index]; FieldInfo fieldInfo = fieldInfos[index];
@ -249,8 +249,8 @@ namespace Nuclex.Support.Cloning {
object clone = FormatterServices.GetUninitializedObject(originalType); object clone = FormatterServices.GetUninitializedObject(originalType);
#endif #endif
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
); );
for(int index = 0; index < fieldInfos.Length; ++index) { for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index]; FieldInfo fieldInfo = fieldInfos[index];

View File

@ -118,8 +118,8 @@ namespace Nuclex.Support.Cloning {
) { ) {
Type originalType = objectToSerialize.GetType(); Type originalType = objectToSerialize.GetType();
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
); );
for(int index = 0; index < fieldInfos.Length; ++index) { for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index]; FieldInfo fieldInfo = fieldInfos[index];
@ -143,8 +143,8 @@ namespace Nuclex.Support.Cloning {
) { ) {
Type originalType = deserializedObject.GetType(); Type originalType = deserializedObject.GetType();
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
); );
for(int index = 0; index < fieldInfos.Length; ++index) { for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index]; FieldInfo fieldInfo = fieldInfos[index];

View File

@ -30,9 +30,9 @@ namespace Nuclex.Support.Collections {
/// priority data type implements the IComparable interface, the user does not /// priority data type implements the IComparable interface, the user does not
/// even /// even
/// </remarks> /// </remarks>
public class PairPriorityQueue<TPriority, TItem> public class PairPriorityQueue<TPriority, TItem> :
: ICollection, IEnumerable<PriorityItemPair<TPriority, TItem>> { ICollection, IEnumerable<PriorityItemPair<TPriority, TItem>> {
#region class PairComparer #region class PairComparer
/// <summary>Compares two priority queue entries based on their priority</summary> /// <summary>Compares two priority queue entries based on their priority</summary>

View File

@ -98,13 +98,12 @@ namespace Nuclex.Support.Collections {
// starting from the last item in the assumption that this is the fastest // starting from the last item in the assumption that this is the fastest
// way to empty a list without causing excessive shiftings in the array. // way to empty a list without causing excessive shiftings in the array.
for(int index = base.Count - 1; index >= 0; --index) { for(int index = base.Count - 1; index >= 0; --index) {
IDisposable disposable = base[index] as IDisposable; IDisposable disposable = base[index] as IDisposable;
// If the item is disposable, destroy it now // If the item is disposable, destroy it now
if(disposable != null) if(disposable != null) {
disposable.Dispose(); disposable.Dispose();
}
} }
base.ClearItems(); base.ClearItems();

View File

@ -45,18 +45,35 @@ namespace Nuclex.Support.Collections {
/// automatically call its IRecyclable.Recycle() method. /// automatically call its IRecyclable.Recycle() method.
/// </para> /// </para>
/// </remarks> /// </remarks>
public class Pool<TItem> where TItem : class, new() { public class Pool<TItem> {
/// <summary>Default number of recyclable objects the pool will store</summary> /// <summary>Default number of recyclable objects the pool will store</summary>
public const int DefaultPoolSize = 64; public const int DefaultPoolSize = 64;
/// <summary>Initializes a new pool using the default capacity</summary> /// <summary>Initializes a new pool using the default capacity</summary>
public Pool() : this(DefaultPoolSize) { } /// <param name="createNewDelegate">Delegate that will be used to create new items</param>
/// <param name="recycleDelegate">Delegate that will be used to recycle items</param>
public Pool(Func<TItem> createNewDelegate = null, Action<TItem> recycleDelegate = null) :
this(DefaultPoolSize, createNewDelegate, recycleDelegate) { }
/// <summary>Initializes a new pool using a user-specified capacity</summary> /// <summary>Initializes a new pool using a user-specified capacity</summary>
/// <param name="capacity">Capacity of the pool</param> /// <param name="capacity">Capacity of the pool</param>
public Pool(int capacity) { /// <param name="createNewDelegate">Delegate that will be used to create new items</param>
/// <param name="recycleDelegate">Delegate that will be used to recycle items</param>
public Pool(
int capacity, Func<TItem> createNewDelegate = null, Action<TItem> recycleDelegate = null
) {
Capacity = capacity; Capacity = capacity;
if(createNewDelegate == null) {
createNewDelegate = new Func<TItem>(Activator.CreateInstance<TItem>);
}
if(recycleDelegate == null) {
recycleDelegate = new Action<TItem>(callRecycleIfSupported);
}
this.createNewDelegate = createNewDelegate;
this.recycleDelegate = recycleDelegate;
} }
/// <summary> /// <summary>
@ -68,7 +85,7 @@ namespace Nuclex.Support.Collections {
if(this.items.Count > 0) { if(this.items.Count > 0) {
return this.items.Dequeue(); return this.items.Dequeue();
} else { } else {
return new TItem(); return this.createNewDelegate();
} }
} }
} }
@ -82,13 +99,14 @@ namespace Nuclex.Support.Collections {
// Call Recycle() when the object is redeemed (instead of when it leaves // Call Recycle() when the object is redeemed (instead of when it leaves
// the pool again) in order to eliminate any references the object may hold // the pool again) in order to eliminate any references the object may hold
// to other objects. // to other objects.
callRecycleIfSupported(item); this.recycleDelegate(item);
lock(this) { lock(this) {
if(this.items.Count < this.capacity) { if(this.items.Count < this.capacity) {
this.items.Enqueue(item); this.items.Enqueue(item);
} }
} }
} }
/// <summary>Number of objects the pool can retain</summary> /// <summary>Number of objects the pool can retain</summary>
@ -125,6 +143,8 @@ namespace Nuclex.Support.Collections {
/// Required because the Queue class doesn't allow this value to be retrieved /// Required because the Queue class doesn't allow this value to be retrieved
/// </remarks> /// </remarks>
private int capacity; private int capacity;
private Func<TItem> createNewDelegate;
private Action<TItem> recycleDelegate;
} }

View File

@ -36,7 +36,7 @@ namespace Nuclex.Support.Plugins {
/// <param name="type">Type which will be assessed</param> /// <param name="type">Type which will be assessed</param>
/// <returns>True if the type can be employed, otherwise false</returns> /// <returns>True if the type can be employed, otherwise false</returns>
public virtual bool CanEmploy(Type type) { public virtual bool CanEmploy(Type type) {
return PluginHelper.HasDefaultConstructor(type); return type.HasDefaultConstructor();
} }
/// <summary>Employs the specified plugin type</summary> /// <summary>Employs the specified plugin type</summary>

View File

@ -24,7 +24,7 @@ using System.Collections.Generic;
namespace Nuclex.Support.Plugins { namespace Nuclex.Support.Plugins {
/// <summary>Employer to create factories of suiting types found in plugins</summary> /// <summary>Employer to create factories of suiting types found in plugins</summary>
/// <typeparam name="ProductType"> /// <typeparam name="TProduct">
/// Interface or base class that the types need to implement /// Interface or base class that the types need to implement
/// </typeparam> /// </typeparam>
/// <remarks> /// <remarks>
@ -41,12 +41,12 @@ namespace Nuclex.Support.Plugins {
/// a human-readable name, capabilities or an icon. /// a human-readable name, capabilities or an icon.
/// </para> /// </para>
/// </remarks> /// </remarks>
public class FactoryEmployer<ProductType> : Employer where ProductType : class { public class FactoryEmployer<TProduct> : Employer where TProduct : class {
#region class ConcreteFactory #region class ConcreteFactory
/// <summary>Concrete factory for the types in a plugin assembly</summary> /// <summary>Concrete factory for the types in a plugin assembly</summary>
private class ConcreteFactory : IAbstractFactory<ProductType>, IAbstractFactory { private class ConcreteFactory : IAbstractFactory<TProduct>, IAbstractFactory {
/// <summary> /// <summary>
/// Initializes a factory and configures it for the specified product /// Initializes a factory and configures it for the specified product
@ -58,8 +58,8 @@ namespace Nuclex.Support.Plugins {
/// <summary>Create a new instance of the type the factory is configured to</summary> /// <summary>Create a new instance of the type the factory is configured to</summary>
/// <returns>The newly created instance</returns> /// <returns>The newly created instance</returns>
public ProductType CreateInstance() { public TProduct CreateInstance() {
return (ProductType)Activator.CreateInstance(this.concreteType); return (TProduct)Activator.CreateInstance(this.concreteType);
} }
/// <summary>Create a new instance of the type the factory is configured to</summary> /// <summary>Create a new instance of the type the factory is configured to</summary>
@ -77,11 +77,11 @@ namespace Nuclex.Support.Plugins {
/// <summary>Initializes a new FactoryEmployer</summary> /// <summary>Initializes a new FactoryEmployer</summary>
public FactoryEmployer() { public FactoryEmployer() {
this.employedFactories = new List<IAbstractFactory<ProductType>>(); this.employedFactories = new List<IAbstractFactory<TProduct>>();
} }
/// <summary>List of all factories that the instance employer has created</summary> /// <summary>List of all factories that the instance employer has created</summary>
public List<IAbstractFactory<ProductType>> Factories { public List<IAbstractFactory<TProduct>> Factories {
get { return this.employedFactories; } get { return this.employedFactories; }
} }
@ -90,20 +90,20 @@ namespace Nuclex.Support.Plugins {
/// <returns>True if the type can be employed</returns> /// <returns>True if the type can be employed</returns>
public override bool CanEmploy(Type type) { public override bool CanEmploy(Type type) {
return return
PluginHelper.HasDefaultConstructor(type) && type.HasDefaultConstructor() &&
typeof(ProductType).IsAssignableFrom(type) && typeof(TProduct).IsAssignableFrom(type) &&
!type.ContainsGenericParameters; !type.ContainsGenericParameters;
} }
/// <summary>Employs the specified plugin type</summary> /// <summary>Employs the specified plugin type</summary>
/// <param name="type">Type to be employed</param> /// <param name="type">Type to be employed</param>
public override void Employ(Type type) { public override void Employ(Type type) {
if(!PluginHelper.HasDefaultConstructor(type)) { if(!type.HasDefaultConstructor()) {
throw new MissingMethodException( throw new MissingMethodException(
"Cannot employ type because it does not have a public default constructor" "Cannot employ type because it does not have a public default constructor"
); );
} }
if(!typeof(ProductType).IsAssignableFrom(type)) { if(!typeof(TProduct).IsAssignableFrom(type)) {
throw new InvalidCastException( throw new InvalidCastException(
"Cannot employ type because it cannot be cast to the factory's product type" "Cannot employ type because it cannot be cast to the factory's product type"
); );
@ -118,7 +118,7 @@ namespace Nuclex.Support.Plugins {
} }
/// <summary>All factories that the instance employer has created</summary> /// <summary>All factories that the instance employer has created</summary>
private List<IAbstractFactory<ProductType>> employedFactories; private List<IAbstractFactory<TProduct>> employedFactories;
} }

View File

@ -24,7 +24,7 @@ using System.Collections.Generic;
namespace Nuclex.Support.Plugins { namespace Nuclex.Support.Plugins {
/// <summary>Employer that directly creates instances of the types in a plugin</summary> /// <summary>Employer that directly creates instances of the types in a plugin</summary>
/// <typeparam name="T">Interface or base class required for the employed types</typeparam> /// <typeparam name="TEmployedType">Interface or base class required for the employed types</typeparam>
/// <remarks> /// <remarks>
/// <para> /// <para>
/// This employer directly creates an instance of any type in a plugin assembly that /// This employer directly creates an instance of any type in a plugin assembly that
@ -44,15 +44,15 @@ namespace Nuclex.Support.Plugins {
/// factory would then be implemented on the plugin side. /// factory would then be implemented on the plugin side.
/// </para> /// </para>
/// </remarks> /// </remarks>
public class InstanceEmployer<T> : Employer { public class InstanceEmployer<TEmployedType> : Employer {
/// <summary>Initializes a new instance employer</summary> /// <summary>Initializes a new instance employer</summary>
public InstanceEmployer() { public InstanceEmployer() {
this.employedInstances = new List<T>(); this.employedInstances = new List<TEmployedType>();
} }
/// <summary>All instances that have been employed</summary> /// <summary>All instances that have been employed</summary>
public List<T> Instances { public List<TEmployedType> Instances {
get { return this.employedInstances; } get { return this.employedInstances; }
} }
@ -61,19 +61,19 @@ namespace Nuclex.Support.Plugins {
/// <returns>True if the type can be employed</returns> /// <returns>True if the type can be employed</returns>
public override bool CanEmploy(Type type) { public override bool CanEmploy(Type type) {
return return
PluginHelper.HasDefaultConstructor(type) && type.HasDefaultConstructor() &&
typeof(T).IsAssignableFrom(type) && typeof(TEmployedType).IsAssignableFrom(type) &&
!type.ContainsGenericParameters; !type.ContainsGenericParameters;
} }
/// <summary>Employs the specified plugin type</summary> /// <summary>Employs the specified plugin type</summary>
/// <param name="type">Type to be employed</param> /// <param name="type">Type to be employed</param>
public override void Employ(Type type) { public override void Employ(Type type) {
this.employedInstances.Add((T)Activator.CreateInstance(type)); this.employedInstances.Add((TEmployedType)Activator.CreateInstance(type));
} }
/// <summary>All instances employed by the instance employer</summary> /// <summary>All instances employed by the instance employer</summary>
private List<T> employedInstances; private List<TEmployedType> employedInstances;
} }

View File

@ -1,83 +0,0 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2012 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.IO;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Plugins {
/// <summary>Unit Test for the plugin helper class</summary>
[TestFixture]
internal class PluginHelperTest {
#region class NoDefaultConstructor
/// <summary>Test class that doesn't have a default constructor</summary>
private class NoDefaultConstructor {
/// <summary>Initializes a new instance of the test class</summary>
/// <param name="dummy">Dummy argument so this is no default constructor</param>
public NoDefaultConstructor(int dummy) { }
}
#endregion // class NoDefaultConstructor
#region class NonPublicDefaultConstructor
/// <summary>Test class that has a non-public default constructor</summary>
private class NonPublicDefaultConstructor {
/// <summary>Initializes a new instance of the test class</summary>
protected NonPublicDefaultConstructor() { }
}
#endregion // class NonPublicDefaultConstructor
#region class PublicDefaultConstructor
/// <summary>Test class that has a public default constructor</summary>
private class PublicDefaultConstructor {
/// <summary>Initializes a new instance of the test class</summary>
public PublicDefaultConstructor() { }
}
#endregion // class PublicDefaultConstructor
/// <summary>Tests whether the default constructor detection works as expected</summary>
[Test]
public void TestDefaultConstructorDetection() {
Assert.IsFalse(
PluginHelper.HasDefaultConstructor(typeof(NoDefaultConstructor))
);
Assert.IsFalse(
PluginHelper.HasDefaultConstructor(typeof(NonPublicDefaultConstructor))
);
Assert.IsTrue(
PluginHelper.HasDefaultConstructor(typeof(PublicDefaultConstructor))
);
}
}
} // namespace Nuclex.Support.Plugins
#endif // UNITTEST

View File

@ -1,44 +0,0 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2012 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Reflection;
namespace Nuclex.Support.Plugins {
/// <summary>Supporting functions for the plugin classes</summary>
public static class PluginHelper {
/// <summary>Determines whether the given type has a default constructor</summary>
/// <param name="type">Type which is to be checked</param>
/// <returns>True if the type has a default constructor</returns>
public static bool HasDefaultConstructor(Type type) {
ConstructorInfo[] constructors = type.GetConstructors();
foreach(ConstructorInfo constructor in constructors)
if(constructor.IsPublic && (constructor.GetParameters().Length == 0))
return true;
return false;
}
}
} // namespace Nuclex.Support.Plugins

View File

@ -7,16 +7,16 @@ namespace Nuclex.Support.Plugins {
#if !NO_CLONING #if !NO_CLONING
/// <summary>Factory that creates instances by cloning a prototype</summary> /// <summary>Factory that creates instances by cloning a prototype</summary>
/// <typeparam name="ProductType">Type of product created by the factory</typeparam> /// <typeparam name="TProduct">Type of product created by the factory</typeparam>
/// <typeparam name="ConcreteType">Type of the prototype that will be cloned</typeparam> /// <typeparam name="TConcreteType">Type of the prototype that will be cloned</typeparam>
public class PrototypeFactory<ProductType, ConcreteType> : public class PrototypeFactory<TProduct, TConcreteType> :
IAbstractFactory<ProductType>, IAbstractFactory, IDisposable IAbstractFactory<TProduct>, IAbstractFactory, IDisposable
where ProductType : class where TProduct : class
where ConcreteType : class, ICloneable { where TConcreteType : class, ICloneable {
/// <summary>Initializes a new prototype based factory</summary> /// <summary>Initializes a new prototype based factory</summary>
/// <param name="prototype">Prototype instance that will be cloned</param> /// <param name="prototype">Prototype instance that will be cloned</param>
public PrototypeFactory(ConcreteType prototype) { public PrototypeFactory(TConcreteType prototype) {
this.prototype = prototype; this.prototype = prototype;
} }
@ -24,8 +24,8 @@ namespace Nuclex.Support.Plugins {
/// Creates a new instance of the type to which the factory is specialized /// Creates a new instance of the type to which the factory is specialized
/// </summary> /// </summary>
/// <returns>The newly created instance</returns> /// <returns>The newly created instance</returns>
public ProductType CreateInstance() { public TProduct CreateInstance() {
return (ProductType)this.prototype.Clone(); return (TProduct)this.prototype.Clone();
} }
/// <summary> /// <summary>
@ -49,7 +49,7 @@ namespace Nuclex.Support.Plugins {
} }
/// <summary>The prototype object</summary> /// <summary>The prototype object</summary>
private ConcreteType prototype; private TConcreteType prototype;
} }

View File

@ -25,11 +25,42 @@ using System.Reflection;
using NUnit.Framework; using NUnit.Framework;
namespace Nuclex.Support.Cloning { namespace Nuclex.Support {
/// <summary>Unit Test for the cloner helpers</summary> /// <summary>Unit Test for the strign segment class</summary>
[TestFixture] [TestFixture]
internal class ClonerHelpersTest { internal class TypeHelperTest {
#region class NoDefaultConstructor
/// <summary>Test class that doesn't have a default constructor</summary>
private class NoDefaultConstructor {
/// <summary>Initializes a new instance of the test class</summary>
/// <param name="dummy">Dummy argument so this is no default constructor</param>
public NoDefaultConstructor(int dummy) { }
}
#endregion // class NoDefaultConstructor
#region class NonPublicDefaultConstructor
/// <summary>Test class that has a non-public default constructor</summary>
private class NonPublicDefaultConstructor {
/// <summary>Initializes a new instance of the test class</summary>
protected NonPublicDefaultConstructor() { }
}
#endregion // class NonPublicDefaultConstructor
#region class PublicDefaultConstructor
/// <summary>Test class that has a public default constructor</summary>
private class PublicDefaultConstructor {
/// <summary>Initializes a new instance of the test class</summary>
public PublicDefaultConstructor() { }
}
#endregion // class PublicDefaultConstructor
#region class Base #region class Base
@ -61,8 +92,8 @@ namespace Nuclex.Support.Cloning {
/// </summary> /// </summary>
[Test] [Test]
public void CanGetBackingFieldsForPropertiesInBaseClasses() { public void CanGetBackingFieldsForPropertiesInBaseClasses() {
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses( FieldInfo[] fieldInfos = typeof(Derived).GetFieldInfosIncludingBaseClasses(
typeof(Derived), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
); );
Assert.AreEqual(4, fieldInfos.Length); Assert.AreEqual(4, fieldInfos.Length);
} }
@ -79,8 +110,16 @@ namespace Nuclex.Support.Cloning {
}; };
} }
/// <summary>Tests whether the default constructor detection works as expected</summary>
[Test]
public void TestDefaultConstructorDetection() {
Assert.IsFalse(typeof(NoDefaultConstructor).HasDefaultConstructor());
Assert.IsFalse(typeof(NonPublicDefaultConstructor).HasDefaultConstructor());
Assert.IsTrue(typeof(PublicDefaultConstructor).HasDefaultConstructor());
}
} }
} // namespace Nuclex.Support.Cloning } // namespace Nuclex.Support
#endif // UNITTEST #endif // UNITTEST

147
Source/TypeHelper.cs Normal file
View File

@ -0,0 +1,147 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2012 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Nuclex.Support {
/// <summary>Helper methods for the reflection Type class</summary>
public static class TypeHelper {
#region class MemberInfoComparer
/// <summary>Determines whether member informations relate to the same member</summary>
private class MemberInfoComparer : IEqualityComparer<MemberInfo> {
/// <summary>Default instance of the comparer</summary>
public static readonly MemberInfoComparer Default = new MemberInfoComparer();
/// <summary>Checks whether two member informations are equal</summary>
/// <param name="left">Informations about the left member in the comaprison</param>
/// <param name="right">Informations about the right member in the comparison</param>
/// <returns>True if the two member informations relate to the same member</returns>
public bool Equals(MemberInfo left, MemberInfo right) {
return
(left.DeclaringType == right.DeclaringType) &&
(left.Name == right.Name);
}
/// <summary>Determines the hash code of the specified member informations</summary>
/// <param name="memberInfo">
/// Member informations whose hash code will be determined
/// </param>
/// <returns>The hash code of the specified member informations</returns>
public int GetHashCode(MemberInfo memberInfo) {
return (memberInfo.DeclaringType.GetHashCode() ^ memberInfo.Name.GetHashCode());
}
}
#endregion // class MemberInfoComparer
/// <summary>Determines whether the given type has a default constructor</summary>
/// <param name="type">Type which is to be checked</param>
/// <returns>True if the type has a default constructor</returns>
public static bool HasDefaultConstructor(this Type type) {
ConstructorInfo[] constructors = type.GetConstructors();
for(int index = 0; index < constructors.Length; ++index) {
ConstructorInfo constructor = constructors[index];
if(constructor.IsPublic && (constructor.GetParameters().Length == 0)) {
return true;
}
}
return false;
}
#if XBOX || WINDOWS_PHONE
/// <summary>
/// Returns all the fields of a type, including those defined in the type's base classes
/// </summary>
/// <param name="type">Type whose fields will be returned</param>
/// <param name="bindingFlags">Binding flags to use when querying the fields</param>
/// <returns>All of the type's fields, including its base types</returns>
public static FieldInfo[] GetFieldInfosIncludingBaseClasses(
this Type type, BindingFlags bindingFlags
) {
FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
// If this class doesn't have a base, don't waste any time
if(type.BaseType != typeof(object)) {
var fieldInfoSet = new Dictionary<FieldInfo, object>(MemberInfoComparer.Default);
for(int index = 0; index < fieldInfos.Length; ++index) {
fieldInfoSet.Add(fieldInfos[index], null);
}
while(type.BaseType != typeof(object)) {
type = type.BaseType;
fieldInfos = type.GetFields(bindingFlags);
for(int index = 0; index < fieldInfos.Length; ++index) {
FieldInfo fieldInfo = fieldInfos[index];
if(!fieldInfoSet.ContainsKey(fieldInfo)) {
fieldInfoSet.Add(fieldInfo, null);
}
}
}
fieldInfos = new FieldInfo[fieldInfoSet.Count];
fieldInfoSet.Keys.CopyTo(fieldInfos, 0);
}
return fieldInfos;
}
#else
/// <summary>
/// Returns all the fields of a type, including those defined in the type's base classes
/// </summary>
/// <param name="type">Type whose fields will be returned</param>
/// <param name="bindingFlags">Binding flags to use when querying the fields</param>
/// <returns>All of the type's fields, including its base types</returns>
public static FieldInfo[] GetFieldInfosIncludingBaseClasses(
this Type type, BindingFlags bindingFlags
) {
FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
// If this class doesn't have a base, don't waste any time
if(type.BaseType != typeof(object)) {
var fieldInfoSet = new HashSet<FieldInfo>(fieldInfos, MemberInfoComparer.Default);
while(type.BaseType != typeof(object)) {
type = type.BaseType;
fieldInfos = type.GetFields(bindingFlags);
for(int index = 0; index < fieldInfos.Length; ++index) {
fieldInfoSet.Add(fieldInfos[index]);
}
}
fieldInfos = new FieldInfo[fieldInfoSet.Count];
fieldInfoSet.CopyTo(fieldInfos);
}
return fieldInfos;
}
#endif
}
} // namespace Nuclex.Support