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:
parent
24439da822
commit
c91a082e84
|
@ -58,10 +58,6 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.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.FieldBased.cs">
|
||||
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
|
||||
|
@ -253,10 +249,6 @@
|
|||
<Compile Include="Source\Plugins\NoPluginAttribute.Test.cs">
|
||||
<DependentUpon>NoPluginAttribute.cs</DependentUpon>
|
||||
</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.Test.cs">
|
||||
<DependentUpon>PluginHost.cs</DependentUpon>
|
||||
|
@ -285,6 +277,10 @@
|
|||
<Compile Include="Source\ObservableHelper.Test.cs">
|
||||
<DependentUpon>ObservableHelper.cs</DependentUpon>
|
||||
</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.Test.cs">
|
||||
<DependentUpon>Semaphore.cs</DependentUpon>
|
||||
|
|
|
@ -89,10 +89,6 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.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.FieldBased.cs">
|
||||
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
|
||||
|
@ -284,10 +280,6 @@
|
|||
<Compile Include="Source\Plugins\NoPluginAttribute.Test.cs">
|
||||
<DependentUpon>NoPluginAttribute.cs</DependentUpon>
|
||||
</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.Test.cs">
|
||||
<DependentUpon>PluginHost.cs</DependentUpon>
|
||||
|
@ -316,6 +308,10 @@
|
|||
<Compile Include="Source\ObservableHelper.Test.cs">
|
||||
<DependentUpon>ObservableHelper.cs</DependentUpon>
|
||||
</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.Test.cs">
|
||||
<DependentUpon>Semaphore.cs</DependentUpon>
|
||||
|
|
|
@ -100,10 +100,6 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.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.FieldBased.cs">
|
||||
<DependentUpon>ExpressionTreeCloner.cs</DependentUpon>
|
||||
|
@ -295,10 +291,6 @@
|
|||
<Compile Include="Source\Plugins\NoPluginAttribute.Test.cs">
|
||||
<DependentUpon>NoPluginAttribute.cs</DependentUpon>
|
||||
</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.Test.cs">
|
||||
<DependentUpon>PluginHost.cs</DependentUpon>
|
||||
|
@ -327,6 +319,10 @@
|
|||
<Compile Include="Source\ObservableHelper.Test.cs">
|
||||
<DependentUpon>ObservableHelper.cs</DependentUpon>
|
||||
</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.Test.cs">
|
||||
<DependentUpon>Semaphore.cs</DependentUpon>
|
||||
|
|
|
@ -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
|
|
@ -185,8 +185,8 @@ namespace Nuclex.Support.Cloning {
|
|||
);
|
||||
|
||||
// Enumerate all of the type's fields and generate transfer expressions for each
|
||||
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
|
||||
clonedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
FieldInfo[] fieldInfos = clonedType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
|
@ -495,8 +495,8 @@ namespace Nuclex.Support.Cloning {
|
|||
ICollection<Expression> transferExpressions
|
||||
) {
|
||||
// Enumerate all of the type's fields and generate transfer expressions for each
|
||||
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
|
||||
clonedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
FieldInfo[] fieldInfos = clonedType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
|
|
|
@ -167,8 +167,8 @@ namespace Nuclex.Support.Cloning {
|
|||
object clone = FormatterServices.GetUninitializedObject(originalType);
|
||||
#endif
|
||||
|
||||
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
|
||||
originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
|
@ -249,8 +249,8 @@ namespace Nuclex.Support.Cloning {
|
|||
object clone = FormatterServices.GetUninitializedObject(originalType);
|
||||
#endif
|
||||
|
||||
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
|
||||
originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
|
|
|
@ -118,8 +118,8 @@ namespace Nuclex.Support.Cloning {
|
|||
) {
|
||||
Type originalType = objectToSerialize.GetType();
|
||||
|
||||
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
|
||||
originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
|
@ -143,8 +143,8 @@ namespace Nuclex.Support.Cloning {
|
|||
) {
|
||||
Type originalType = deserializedObject.GetType();
|
||||
|
||||
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
|
||||
originalType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
FieldInfo[] fieldInfos = originalType.GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
for(int index = 0; index < fieldInfos.Length; ++index) {
|
||||
FieldInfo fieldInfo = fieldInfos[index];
|
||||
|
|
|
@ -30,8 +30,8 @@ namespace Nuclex.Support.Collections {
|
|||
/// priority data type implements the IComparable interface, the user does not
|
||||
/// even
|
||||
/// </remarks>
|
||||
public class PairPriorityQueue<TPriority, TItem>
|
||||
: ICollection, IEnumerable<PriorityItemPair<TPriority, TItem>> {
|
||||
public class PairPriorityQueue<TPriority, TItem> :
|
||||
ICollection, IEnumerable<PriorityItemPair<TPriority, TItem>> {
|
||||
|
||||
#region class PairComparer
|
||||
|
||||
|
|
|
@ -98,13 +98,12 @@ namespace Nuclex.Support.Collections {
|
|||
// 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.
|
||||
for(int index = base.Count - 1; index >= 0; --index) {
|
||||
|
||||
IDisposable disposable = base[index] as IDisposable;
|
||||
|
||||
// If the item is disposable, destroy it now
|
||||
if(disposable != null)
|
||||
if(disposable != null) {
|
||||
disposable.Dispose();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
base.ClearItems();
|
||||
|
|
|
@ -45,18 +45,35 @@ namespace Nuclex.Support.Collections {
|
|||
/// automatically call its IRecyclable.Recycle() method.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class Pool<TItem> where TItem : class, new() {
|
||||
public class Pool<TItem> {
|
||||
|
||||
/// <summary>Default number of recyclable objects the pool will store</summary>
|
||||
public const int DefaultPoolSize = 64;
|
||||
|
||||
/// <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>
|
||||
/// <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;
|
||||
|
||||
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>
|
||||
|
@ -68,7 +85,7 @@ namespace Nuclex.Support.Collections {
|
|||
if(this.items.Count > 0) {
|
||||
return this.items.Dequeue();
|
||||
} 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
|
||||
// the pool again) in order to eliminate any references the object may hold
|
||||
// to other objects.
|
||||
callRecycleIfSupported(item);
|
||||
this.recycleDelegate(item);
|
||||
|
||||
lock(this) {
|
||||
if(this.items.Count < this.capacity) {
|
||||
this.items.Enqueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <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
|
||||
/// </remarks>
|
||||
private int capacity;
|
||||
private Func<TItem> createNewDelegate;
|
||||
private Action<TItem> recycleDelegate;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace Nuclex.Support.Plugins {
|
|||
/// <param name="type">Type which will be assessed</param>
|
||||
/// <returns>True if the type can be employed, otherwise false</returns>
|
||||
public virtual bool CanEmploy(Type type) {
|
||||
return PluginHelper.HasDefaultConstructor(type);
|
||||
return type.HasDefaultConstructor();
|
||||
}
|
||||
|
||||
/// <summary>Employs the specified plugin type</summary>
|
||||
|
|
|
@ -24,7 +24,7 @@ using System.Collections.Generic;
|
|||
namespace Nuclex.Support.Plugins {
|
||||
|
||||
/// <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
|
||||
/// </typeparam>
|
||||
/// <remarks>
|
||||
|
@ -41,12 +41,12 @@ namespace Nuclex.Support.Plugins {
|
|||
/// a human-readable name, capabilities or an icon.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class FactoryEmployer<ProductType> : Employer where ProductType : class {
|
||||
public class FactoryEmployer<TProduct> : Employer where TProduct : class {
|
||||
|
||||
#region class ConcreteFactory
|
||||
|
||||
/// <summary>Concrete factory for the types in a plugin assembly</summary>
|
||||
private class ConcreteFactory : IAbstractFactory<ProductType>, IAbstractFactory {
|
||||
private class ConcreteFactory : IAbstractFactory<TProduct>, IAbstractFactory {
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// <returns>The newly created instance</returns>
|
||||
public ProductType CreateInstance() {
|
||||
return (ProductType)Activator.CreateInstance(this.concreteType);
|
||||
public TProduct CreateInstance() {
|
||||
return (TProduct)Activator.CreateInstance(this.concreteType);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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>
|
||||
public List<IAbstractFactory<ProductType>> Factories {
|
||||
public List<IAbstractFactory<TProduct>> Factories {
|
||||
get { return this.employedFactories; }
|
||||
}
|
||||
|
||||
|
@ -90,20 +90,20 @@ namespace Nuclex.Support.Plugins {
|
|||
/// <returns>True if the type can be employed</returns>
|
||||
public override bool CanEmploy(Type type) {
|
||||
return
|
||||
PluginHelper.HasDefaultConstructor(type) &&
|
||||
typeof(ProductType).IsAssignableFrom(type) &&
|
||||
type.HasDefaultConstructor() &&
|
||||
typeof(TProduct).IsAssignableFrom(type) &&
|
||||
!type.ContainsGenericParameters;
|
||||
}
|
||||
|
||||
/// <summary>Employs the specified plugin type</summary>
|
||||
/// <param name="type">Type to be employed</param>
|
||||
public override void Employ(Type type) {
|
||||
if(!PluginHelper.HasDefaultConstructor(type)) {
|
||||
if(!type.HasDefaultConstructor()) {
|
||||
throw new MissingMethodException(
|
||||
"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(
|
||||
"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>
|
||||
private List<IAbstractFactory<ProductType>> employedFactories;
|
||||
private List<IAbstractFactory<TProduct>> employedFactories;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ using System.Collections.Generic;
|
|||
namespace Nuclex.Support.Plugins {
|
||||
|
||||
/// <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>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class InstanceEmployer<T> : Employer {
|
||||
public class InstanceEmployer<TEmployedType> : Employer {
|
||||
|
||||
/// <summary>Initializes a new instance employer</summary>
|
||||
public InstanceEmployer() {
|
||||
this.employedInstances = new List<T>();
|
||||
this.employedInstances = new List<TEmployedType>();
|
||||
}
|
||||
|
||||
/// <summary>All instances that have been employed</summary>
|
||||
public List<T> Instances {
|
||||
public List<TEmployedType> Instances {
|
||||
get { return this.employedInstances; }
|
||||
}
|
||||
|
||||
|
@ -61,19 +61,19 @@ namespace Nuclex.Support.Plugins {
|
|||
/// <returns>True if the type can be employed</returns>
|
||||
public override bool CanEmploy(Type type) {
|
||||
return
|
||||
PluginHelper.HasDefaultConstructor(type) &&
|
||||
typeof(T).IsAssignableFrom(type) &&
|
||||
type.HasDefaultConstructor() &&
|
||||
typeof(TEmployedType).IsAssignableFrom(type) &&
|
||||
!type.ContainsGenericParameters;
|
||||
}
|
||||
|
||||
/// <summary>Employs the specified plugin type</summary>
|
||||
/// <param name="type">Type to be employed</param>
|
||||
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>
|
||||
private List<T> employedInstances;
|
||||
private List<TEmployedType> employedInstances;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -7,16 +7,16 @@ namespace Nuclex.Support.Plugins {
|
|||
#if !NO_CLONING
|
||||
|
||||
/// <summary>Factory that creates instances by cloning a prototype</summary>
|
||||
/// <typeparam name="ProductType">Type of product created by the factory</typeparam>
|
||||
/// <typeparam name="ConcreteType">Type of the prototype that will be cloned</typeparam>
|
||||
public class PrototypeFactory<ProductType, ConcreteType> :
|
||||
IAbstractFactory<ProductType>, IAbstractFactory, IDisposable
|
||||
where ProductType : class
|
||||
where ConcreteType : class, ICloneable {
|
||||
/// <typeparam name="TProduct">Type of product created by the factory</typeparam>
|
||||
/// <typeparam name="TConcreteType">Type of the prototype that will be cloned</typeparam>
|
||||
public class PrototypeFactory<TProduct, TConcreteType> :
|
||||
IAbstractFactory<TProduct>, IAbstractFactory, IDisposable
|
||||
where TProduct : class
|
||||
where TConcreteType : class, ICloneable {
|
||||
|
||||
/// <summary>Initializes a new prototype based factory</summary>
|
||||
/// <param name="prototype">Prototype instance that will be cloned</param>
|
||||
public PrototypeFactory(ConcreteType prototype) {
|
||||
public PrototypeFactory(TConcreteType 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
|
||||
/// </summary>
|
||||
/// <returns>The newly created instance</returns>
|
||||
public ProductType CreateInstance() {
|
||||
return (ProductType)this.prototype.Clone();
|
||||
public TProduct CreateInstance() {
|
||||
return (TProduct)this.prototype.Clone();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -49,7 +49,7 @@ namespace Nuclex.Support.Plugins {
|
|||
}
|
||||
|
||||
/// <summary>The prototype object</summary>
|
||||
private ConcreteType prototype;
|
||||
private TConcreteType prototype;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,42 @@ using System.Reflection;
|
|||
|
||||
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]
|
||||
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
|
||||
|
||||
|
@ -61,8 +92,8 @@ namespace Nuclex.Support.Cloning {
|
|||
/// </summary>
|
||||
[Test]
|
||||
public void CanGetBackingFieldsForPropertiesInBaseClasses() {
|
||||
FieldInfo[] fieldInfos = ClonerHelpers.GetFieldInfosIncludingBaseClasses(
|
||||
typeof(Derived), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
FieldInfo[] fieldInfos = typeof(Derived).GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
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
|
147
Source/TypeHelper.cs
Normal file
147
Source/TypeHelper.cs
Normal 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
|
Loading…
Reference in New Issue
Block a user