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>
<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>

View File

@ -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>

View File

@ -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>

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
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];

View File

@ -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];

View File

@ -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];

View File

@ -30,9 +30,9 @@ 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
/// <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
// 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();

View File

@ -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;
}

View File

@ -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>

View File

@ -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;
}

View File

@ -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;
}

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
/// <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;
}

View File

@ -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
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