Override Dictionary.Add - c#

I need to know how to override the Add-method of a certain Dictionary in a certain static class. Any suggestions?
If it matters, the dictionary looks like this:
public static Dictionary<MyEnum,MyArray[]>
Any suggestions?

You can't override the Add method of Dictionary<,> since it's non virtual. You can hide it by adding a method with the same name/signature in the derived class, but hiding isn't the same as overriding. If somebody casts to the base class he will still call the wrong Add.
The correct way to do this is to create your own class that implements IDictionary<,> (the interface) but has a Dictionary<,> (the class) instead of being a Dictionary<,>.
class MyDictionary<TKey,TValue>:IDictionary<TKey,TValue>
{
private Dictionary<TKey,TValue> backingDictionary;
//Implement the interface here
//Delegating most of the logic to your backingDictionary
...
}

I went with CodesInChaos's solution. Since there's a bit of overhead involved in implementing IDictionary<TKey, TValue>, here is a virtual implementation for general consumption. I'm including some comments and an example below for optional reading. Let me know if I should fix or improve something. I haven't done much testing, but it seems to work for overriding the add method.
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Represents a collection of keys and values. This is an abstract base-class wrapping a <see cref="T:System.Collections.Generic.Dictionary`2"/> with
/// virtual method that can be overridden. This class can be used to override the default functionality of <see cref="T:System.Collections.Generic.Dictionary`2"/>.
/// </summary>
/// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
/// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
public class VirtualDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
protected IDictionary<TKey, TValue> wrappedDictionary;
/// <summary>
/// Initializes a new instance of the <see cref="VirtualDictionary{TKey,TValue}"/> class that is empty, has the default initial capacity, and uses the default equality comparer for the key type.
/// </summary>
public VirtualDictionary()
{
wrappedDictionary = new Dictionary<TKey, TValue>();
}
/// <summary>
/// Initializes a new instance of the <see cref="VirtualDictionary{TKey,TValue}"/> class that is empty, has the specified initial capacity, and uses the default equality comparer for the key type.
/// </summary>
/// <param name="capacity">The initial number of elements that the <see cref="T:System.Collections.Generic.VirtualDictionary`2"/> can contain.</param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="capacity"/> is less than 0.</exception>
public VirtualDictionary(int capacity)
{
wrappedDictionary = new Dictionary<TKey, TValue>(capacity);
}
/// <summary>
/// Initializes a new instance of the <see cref="VirtualDictionary{TKey,TValue}"/> class that is empty, has the default initial capacity, and uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer`1"/>.
/// </summary>
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer`1"/> implementation to use when comparing keys, or null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1"/> for the type of the key.</param>
public VirtualDictionary(IEqualityComparer<TKey> comparer)
{
wrappedDictionary = new Dictionary<TKey, TValue>(comparer);
}
/// <summary>
/// Initializes a new instance of the <see cref="VirtualDictionary{TKey,TValue}"/> class that is empty, has the specified initial capacity, and uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer`1"/>.
/// </summary>
/// <param name="capacity">The initial number of elements that the <see cref="VirtualDictionary{TKey,TValue}"/> can contain.</param><param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer`1"/> implementation to use when comparing keys, or null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1"/> for the type of the key.</param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="capacity"/> is less than 0.</exception>
public VirtualDictionary(int capacity, IEqualityComparer<TKey> comparer)
{
wrappedDictionary = new Dictionary<TKey, TValue>(capacity, comparer);
}
/// <summary>
/// Initializes a new instance of the <see cref="VirtualDictionary{TKey,TValue}"/> class that contains elements copied from the specified <see cref="T:System.Collections.Generic.IDictionary`2"/> and uses the default equality comparer for the key type.
/// </summary>
/// <param name="dictionary">The <see cref="T:System.Collections.Generic.IDictionary`2"/> whose elements are copied to the new <see cref="VirtualDictionary{TKey,TValue}"/>.</param><exception cref="T:System.ArgumentNullException"><paramref name="dictionary"/> is null.</exception><exception cref="T:System.ArgumentException"><paramref name="dictionary"/> contains one or more duplicate keys.</exception>
public VirtualDictionary(IDictionary<TKey, TValue> dictionary)
{
wrappedDictionary = new Dictionary<TKey, TValue>(dictionary);
}
/// <summary>
/// Initializes a new instance of the <see cref="VirtualDictionary{TKey,TValue}"/> class that contains elements copied from the specified <see cref="T:System.Collections.Generic.IDictionary`2"/> and uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer`1"/>.
/// </summary>
/// <param name="dictionary">The <see cref="T:System.Collections.Generic.IDictionary`2"/> whose elements are copied to the new <see cref="VirtualDictionary{TKey,TValue}"/>.</param><param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer`1"/> implementation to use when comparing keys, or null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1"/> for the type of the key.</param><exception cref="T:System.ArgumentNullException"><paramref name="dictionary"/> is null.</exception><exception cref="T:System.ArgumentException"><paramref name="dictionary"/> contains one or more duplicate keys.</exception>
public VirtualDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
{
wrappedDictionary = new Dictionary<TKey, TValue>(dictionary, comparer);
}
/// <summary>
/// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </summary>
/// <param name="key">The object to use as the key of the element to add.</param><param name="value">The object to use as the value of the element to add.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception><exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.</exception><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
public virtual void Add(TKey key, TValue value)
{
wrappedDictionary.Add(key, value);
}
/// <summary>
/// Determines whether the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the specified key.
/// </summary>
/// <returns>
/// true if the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the key; otherwise, false.
/// </returns>
/// <param name="key">The key to locate in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
public virtual bool ContainsKey(TKey key)
{
return wrappedDictionary.ContainsKey(key);
}
/// <summary>
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </returns>
public virtual ICollection<TKey> Keys
{
get
{
return wrappedDictionary.Keys;
}
}
/// <summary>
/// Removes the element with the specified key from the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </summary>
/// <returns>
/// true if the element is successfully removed; otherwise, false. This method also returns false if <paramref name="key"/> was not found in the original <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </returns>
/// <param name="key">The key of the element to remove.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
public virtual bool Remove(TKey key)
{
return wrappedDictionary.Remove(key);
}
/// <summary>
/// Gets the value associated with the specified key.
/// </summary>
/// <returns>
/// true if the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the specified key; otherwise, false.
/// </returns>
/// <param name="key">The key whose value to get.</param><param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
public virtual bool TryGetValue(TKey key, out TValue value)
{
return wrappedDictionary.TryGetValue(key, out value);
}
/// <summary>
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </returns>
public virtual ICollection<TValue> Values
{
get
{
return wrappedDictionary.Values;
}
}
/// <summary>
/// Gets or sets the element with the specified key.
/// </summary>
/// <returns>
/// The element with the specified key.
/// </returns>
/// <param name="key">The key of the element to get or set.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception><exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and <paramref name="key"/> is not found.</exception><exception cref="T:System.NotSupportedException">The property is set and the <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
public virtual TValue this[TKey key]
{
get
{
return wrappedDictionary[key];
}
set
{
wrappedDictionary[key] = value;
}
}
/// <summary>
/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
public virtual void Add(KeyValuePair<TKey, TValue> item)
{
wrappedDictionary.Add(item);
}
/// <summary>
/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
public virtual void Clear()
{
wrappedDictionary.Clear();
}
/// <summary>
/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
/// </summary>
/// <returns>
/// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
/// </returns>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
public virtual bool Contains(KeyValuePair<TKey, TValue> item)
{
return wrappedDictionary.Contains(item);
}
/// <summary>
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
/// </summary>
/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException">The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.</exception>
public virtual void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
wrappedDictionary.CopyTo(array, arrayIndex);
}
/// <summary>
/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <returns>
/// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
public virtual int Count
{
get
{
return wrappedDictionary.Count;
}
}
/// <summary>
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
/// </summary>
/// <returns>
/// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
/// </returns>
public virtual bool IsReadOnly
{
get { return wrappedDictionary.IsReadOnly; }
}
/// <summary>
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <returns>
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
public virtual bool Remove(KeyValuePair<TKey, TValue> item)
{
return wrappedDictionary.Remove(item);
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>1</filterpriority>
public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return wrappedDictionary.GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>2</filterpriority>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
My use case was to be able to run the same code to manipulate a DataTable and a kendo datasource result which is IDictionary. So, I wrapped the DataTable with my custom IDictionary such that updates to the IDictionary were made on the DataRow. Maybe someone will find that useful and should serve as an example implementation.
public class DataRowDictionaryWrapper : VirtualDictionary<string, object>
{
private DataRow row;
public DataRowDictionaryWrapper(DataRow row)
{
this.row = row;
this.wrappedDictionary = row.Table.Columns.Cast<DataColumn>().ToDictionary(key => key.ColumnName, c => row[c]);
}
public override void Add(string key, object value)
{
DataColumn col = row.Table.Columns[key];
if (col == null)
{
col = new DataColumn(key);
row.Table.Columns.Add(col);
}
row[col] = value;
base[key] = value;
}
}
public class DataTableDictionaryWrapper : IEnumerable<IDictionary<string, object>>
{
private DataTable dt;
public DataTableDictionaryWrapper(DataTable dt)
{
this.dt = dt;
}
public IEnumerator<IDictionary<string, object>> GetEnumerator()
{
foreach (DataRow row in dt.Rows)
{
yield return new DataRowDictionaryWrapper(row);
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}

It's actually simpler to use the "new" keyword. I do this often when my objects contain thier own unique ID value.
// Warning Air Code - Subject to bonehead mistakes
internal sealed class SomeDict : Dictionary<myKeyType, myObjectType>
{
public new void Add(myKeyType key, myObjectType value)
{
this.Add(value);
}
internal void Add(myObjectType value)
{
base.Add(value.Id, value);
}
}

In some scenarios, where TKey is a property of TValue, you may want to consider using KeyedCollection<TKey, TValue>, which allows you to override InsertItem, SetItem, RemoveItem, and ClearItems.
It uses a dictionary internally (unless specified otherwise via the threshold constructor parameter), but also manages a collection.
I Haven't benchmarked or compared performance, there might be a performance difference so use carefully.
Consider voting on this feature suggestion on the .NET Core repo.

Another "quick and dirty" fix
If you need to add some extra logic to the Add method, you can create your own method, with a slightly different signature, and use THAT in your code.
public class MyOwnDictionary<TKey, TValue>: Dictionary<TKey, TValue>
{
public void AddPlus(TKey key, TValue value)
{
//some custom logic here
base.Add(key,value);
}
}
(but this can lead to maintenance difficulties)

Related

How to reference type parameter in XML documentation?

How do you reference a type parameter in XML code documentation? For instance, this code
/// <summary>
/// An interface.
/// </summary>
/// <typeparam name="TInterface">Type paramter.</typeparam>
public interface IFace<TInterface>
{
/// <summary>
/// Does the thing.
/// </summary>
/// <typeparam name="TMethod">Different from <see cref="TInterface"/>.</typeparam>
/// <returns>An integer.</returns>
int SomeMethod<TMethod>();
}
Gives a warning about typeparam name="TMethod":
XML comment has cref attribute 'TInterface' that refers to a type paramter.
This question asks about referencing a generic type, but I want to reference the type parameter.
Instead of using see cref, the typeparamref should be used instead:
/// <summary>
/// An interface.
/// </summary>
/// <typeparam name="TInterface">Type paramter.</typeparam>
public interface IFace<TInterface>
{
/// <summary>
/// Does the thing.
/// </summary>
/// <typeparam name="TMethod">Different from <typeparamref name="TInterface"/>.</typeparam>
/// <returns>An integer.</returns>
int SomeMethod<TMethod>();
}

How to resolve all service types for the full hierarchy of a closed generic's closure type

I have recently begun using autofac, migrating away from structuremap and lamar. With the previous dependency injection tools it was seemingly rather trivial to resolve all services that closed on the full hierarchy (all interfaces, and base classes) of a given type.
For example structuremap / lamar simply required scanning
// ... registry stuff...
Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.ConnectImplementationsToTypesClosing(typeof(IAnimalFeeder<>));
});
and resolution was just a matter of calling GetAllInstances on the context:
var openFeederType = typeof(IAnimalFeeder<>);
var animalType = typeof(animal);
var closedType = openFeederType.MakeGenericType(animalType);
var feeders = context.GetAllInstances(closedType) // resolve all feeders for given animal hierarchy
However, this does not appear to be the case with autofac. Unless I'm missing some documentation and missuderstanding methods it appears that I need to jump though a number of reflection calls in order to manually determine what the type hierarchy of my service type is, close a generic on each discovered type, close that generic on an IEnumerable and finally make a Resolve call on each closed IEnumerable.
What follows is my approach, and I want to be sure it is valid and not reimplementing existing functionalty
Given our favorite polymorphic animal examples
public interface IAnimal { }
public class Dog : AbstractAnimal { }
public class Wolf : Dog { }
public class Cat : AbstractAnimal { }
I want to discover (Resolve) all feeders, defined below, that can feed an animal
public interface IAnimalFeeder<in TAnimal> { }
public abstract class AbstractAnimal : IAnimal { }
public class AnimalFeeder : IAnimalFeeder<IAnimal> { }
public class ObjectFeeder : IAnimalFeeder<object> { }
public class DogSnackFeeder : IAnimalFeeder<Dog> { }
public class DogFeeder : IAnimalFeeder<Dog> { }
public class WolfFeeder : IAnimalFeeder<Wolf> { }
public class CatFeeder : IAnimalFeeder<Cat> { }
eg:
A DogFeeder can feed both Dog and Wolf, but not Cat
An ObjectFeeder can feed Dog, Wolf, Cat, but not IAnimal
An AnimalFeeder can feed Dog, Wolf, Cat, and IAnimal
etc.
To prove this out I've written an xuint theory with a custom reflection based Resolve in the static ResolutionHelper class (ResolveManyClosedGenericsOfTypeHierarchy) to do my resolution
public class GenericResolveTests
{
[Theory]
[InlineData(new[] {typeof(ObjectFeeder), typeof(AnimalFeeder), typeof(DogFeeder), typeof(DogSnackFeeder)}, typeof(Dog))]
[InlineData(new[] {typeof(ObjectFeeder), typeof(AnimalFeeder), typeof(DogFeeder), typeof(DogSnackFeeder), typeof(WolfFeeder)}, typeof(Wolf))]
[InlineData(new[] {typeof(ObjectFeeder), typeof(AnimalFeeder), typeof(CatFeeder)}, typeof(Cat))]
[InlineData(new[] {typeof(ObjectFeeder)}, typeof(object))]
[InlineData(new[] {typeof(AnimalFeeder)}, typeof(IAnimal))]
public void Generic_Resolve_Test(Type[] expectedTypes,
Type closureType)
{
// Arrange
var assembly = Assembly.GetExecutingAssembly();
var builder = new ContainerBuilder();
// Registration
builder.RegisterAssemblyTypes(assembly) // Register sourcing from the given assembly
.PublicOnly() // Only register public classes
.AsClosedTypesOf(typeof(IAnimalFeeder<>)) // Register types that are assignable to a closed instance of the open generic type IAnimalFeeder<>
.AsSelf() // Register types as themselves (explicit concrete types are made available)
.AsImplementedInterfaces(); // Register the type as providing all of its public interfaces as services
var container = builder.Build();
// Act
// Resolution with non-standard autofac Resolve functionality
var result = container.ResolveManyClosedGenericsOfTypeHierarchy(closureType, typeof(IAnimalFeeder<>));
// Assert
Assert.NotNull(result);
var results = result.ToList();
Assert.Equal(expectedTypes.Length, results.Count);
var resultTypes = results.Select(r => r.GetType())
.ToList();
Assert.All(expectedTypes,
expectedType => Assert.Contains(expectedType, resultTypes));
}
}
public static class ResolutionHelper
{
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <typeparamref name="TServiceClosure" />
/// </summary>
/// <typeparam name="TServiceClosure">the service closure type</typeparam>
/// <param name="componentContext">the component context used for resolution</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <typeparamref name="TServiceClosure" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy<TServiceClosure>(this IComponentContext componentContext,
Type openGenericType)
{
return ResolveManyClosedGenericsOfTypeHierarchy(componentContext.Resolve, typeof(TServiceClosure), openGenericType);
}
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <typeparamref name="TServiceClosure" />
/// </summary>
/// <typeparam name="TServiceClosure">the service closure type</typeparam>
/// <param name="container">the container used for resolution</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <typeparamref name="TServiceClosure" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy<TServiceClosure>(this IContainer container,
Type openGenericType)
{
return ResolveManyClosedGenericsOfTypeHierarchy(container.Resolve, typeof(TServiceClosure), openGenericType);
}
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </summary>
/// <param name="componentContext">the component context used for resolution</param>
/// <param name="serviceClosureType">the service closure type</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy(this IComponentContext componentContext,
Type serviceClosureType,
Type openGenericType)
{
return ResolveManyClosedGenericsOfTypeHierarchy(componentContext.Resolve, serviceClosureType, openGenericType);
}
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </summary>
/// <param name="container">the container used for resolution</param>
/// <param name="serviceClosureType">the service closure type</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy(this IContainer container,
Type serviceClosureType,
Type openGenericType)
{
return ResolveManyClosedGenericsOfTypeHierarchy(container.Resolve, serviceClosureType, openGenericType);
}
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </summary>
/// <param name="resolver">
/// a resolution <see cref="Func{TInput, TResult}" /> that resolves <see cref="Type" /> input into
/// an <see cref="object" />
/// </param>
/// <param name="serviceClosureType">the service closure type</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy(Func<Type, object> resolver,
Type serviceClosureType,
Type openGenericType)
{
if (resolver == null)
{
throw new ArgumentNullException(nameof(resolver));
}
if (serviceClosureType == null)
{
throw new ArgumentNullException(nameof(serviceClosureType));
}
if (openGenericType == null)
{
throw new ArgumentNullException(nameof(openGenericType));
}
if (!openGenericType.IsGenericTypeDefinition)
{
throw new ArgumentException("type must be a generic type definition", nameof(openGenericType));
}
var typesToResolve = GetAllClosedGenericOfTypeHierarchy(serviceClosureType, openGenericType);
return _()
.SelectMany(o => o);
IEnumerable<IEnumerable<object>> _()
{
foreach (var type in typesToResolve)
{
yield return (IEnumerable<object>) resolver(type);
}
}
}
/// <summary>
/// Create a closed generic type of <see cref="openGenericType" /> for each <see cref="type" /> and its implementing
/// <see langwod="interface" /> and base class
/// </summary>
/// <param name="type">the type to find linage of</param>
/// <param name="openGenericType">the open generic to make closed types of</param>
/// <returns>a collection of closed generic types</returns>
private static IEnumerable<Type> GetAllClosedGenericOfTypeHierarchy(Type type,
Type openGenericType)
{
return _();
IEnumerable<Type> _()
{
foreach (var selfAndSuper in GetSelfAndSupers(type))
{
var closedFeederType = openGenericType.MakeGenericType(selfAndSuper);
yield return typeof(IEnumerable<>).MakeGenericType(closedFeederType);
}
}
}
/// <summary>
/// Given a <see cref="Type" /> <paramref name="inputType" /> return a collection of <paramref name="inputType" />, all
/// of <paramref name="inputType" />'s interfaces, and all of its base classes
/// </summary>
/// <param name="inputType">the type to determine linage of</param>
/// <returns>
/// a collection of type including <paramref name="inputType" />, all of its interfaces, and all of its base
/// classes
/// </returns>
private static IEnumerable<Type> GetSelfAndSupers(Type inputType)
{
return _()
.Distinct();
IEnumerable<Type> _()
{
// return self
yield return inputType;
// return interfaces
foreach (var t in inputType.GetInterfaces())
{
yield return t;
}
// return base types
var baseType = inputType.BaseType;
while (baseType != null)
{
yield return baseType;
baseType = baseType.BaseType;
}
}
}
}
Am I jumping though hoops I shouldn't be to make this happen?
Just for consistency sake, this is the approach I'm taking for DTO validation, where animals are various types of DTOs and the "feeders" are explicit units of DTO validation.

StyleCop SA1620, I don't know how to resolve my XML Comments to its liking

I have the following code w/comments: (It does compile)
/// <summary>
/// return a passing result of a particular type
/// </summary>
/// <typeparam name="T">Type of the value to be returned</typeparam>
/// <param name="value">the value to be returned</param>
/// <returns>a passing result</returns>
public static Result<T> Pass(T value)
{
return new Result<T>()
{
Passed = true,
Value = value
};
}
I get the following warning with it:
Warning 1 SA1620 : CSharp.Documentation :
The typeparam tags in the documentation header must
match the generic types for the method.
I did look at the help page for this error, which gives this explanation:
To fix a violation of this rule, add and fill-in one tag
for each generic type parameter on the element, and make sure that
the tags appear in the same order as the element’s type
parameters.
And it has provided sample code:
/// <summary>
/// A sample generic class.
/// </summary>
/// <typeparam name="S">The first generic type parameter.</typeparam>
/// <typeparam name="T">The second generic type parameter.</typeparam>
public class Class1<S, T>
{
}
I don't see anything about mine that breaks the standards it is showing, and I have tried various odd things, but I have no real idea of what I'm supposed to do here.
The only way that this can compile is if this method is inside a class that is generic in T. This method doesn't have any type parameters. If it was generic, there would be type parameters after the name of the method:
public static Result<T> Pass<T>(T value)
{
return new Result<T>()
{
Passed = true,
Value = value
};
}
But that's not the case with your method. So it must be:
class SomeClass<T>
{
public static Result<T> Pass(T value)
{
return new Result<T>()
{
Passed = true,
Value = value
};
}
}
And any documentation about the type parameter belongs up at the class level. E.g.:
/// <summary>
/// This is a result class
/// </summary>
/// <typeparam name="T">Type of the value to be returned</typeparam>
public class Result<T>
{
public bool Passed { get; set; }
public T Value { get; set; }
/// <summary>
/// return a passing result of a particular type
/// </summary>
/// <param name="value">the value to be returned</param>
/// <returns>a passing result</returns>
public static Result<T> Pass(T value)
{
return new Result<T>()
{
Passed = true,
Value = value
};
}
}

Moles and AppSettingsReader?

I'm working with Moles to write some unit tests. I searched online but I don't see any responses on how to use Moles to intercept the calls to AppSettingsReader.GetValue.
Has anyone been able to do this using Moles? Or am I forced into isolating the calls in my own class I can inject or mock? Ideally there is a way to directly use Moles to intercept the calls because we don't really want to modify the code we're looking to put under test.
Thanks!
Firstly, I strongly recommend moving to the release version of Moles, called "Fakes and Stubs", in .NET 4.5 / C# 5 / Visual Studio 2012.
The System.Configurations namespace is incompatible with the Mole/Fake type, and must be stubbed. To create a stub using the Moles Framework, simply create an interface for the System.Configuration.AppSettingsReader type. The Moles compiler will automatically convert the interface into a Stub type.
Here's an interface you can add to your project:
using System;
namespace YOUR_NAMESPACE_HERE
{
/// <summary>
/// IOC object for stubbing System.Configuration.AppSettingsReader.
/// Provides a method for reading values of a particular type from
/// the configuration.
/// </summary>
interface IAppSettingsReader
{
/// <summary>
/// Gets the value for a specified key from the
/// System.Configuration.ConfigurationSettings.AppSettings property
/// and returns an object of the specified type containing the
/// value from the configuration.
/// </summary>
/// <param name="key">The key for which to get the value.</param>
/// <param name="type">The type of the object to return.</param>
/// <returns>The value of the specified key</returns>
/// <exception cref="System.ArgumentNullException">key is null.
/// - or -
/// type is null.</exception>
/// <exception cref="System.InvalidOperationException">key does
/// not exist in the <appSettings> configuration section.
/// - or -
/// The value in the <appSettings> configuration section
/// for key is not of type type.</exception>
public object GetValue(string key, Type type);
}
}
Here's a stub class, too:
using System;
using System.Configuration;
namespace YOUR_NAMESPACE_HERE
{
/// <summary>
/// Production stub for System.Configuration.AppSettingsReader.
/// Provides a method for reading values of a particular type from
/// the configuration.
/// </summary>
public class AppSettingsReaderStub : IAppSettingsReader
{
/// <summary>
/// Gets the value for a specified key from the
/// System.Configuration.ConfigurationSettings.AppSettings property
/// and returns an object of the specified type containing the value
/// from the configuration.
/// </summary>
/// <param name="key">The key for which to get the value.</param>
/// <param name="type">The type of the object to return.</param>
/// <returns>The value of the specified key</returns>
/// <exception cref="System.ArgumentNullException">key is null.
/// - or -
/// type is null.</exception>
/// <exception cref="System.InvalidOperationException">key does not
/// exist in the <appSettings> configuration section.
/// - or -
/// The value in the <appSettings> configuration section for
/// key is not of type type.</exception>
public object GetValue(string key, Type type)
{
var reader = new AppSettingsReader();
object result = reader.GetValue(key, type);
return result;
}
}
}

Best way to translate from IDictionary to a generic IDictionary

I've got an IDictionary field that I would like to expose via a property of type IDictionary<string, dynamic> the conversion is surprisingly difficult since I have no idea what I can .Cast<>() the IDictionary to.
Best I've got:
IDictionary properties;
protected virtual IDictionary<string, dynamic> Properties {
get {
return _properties.Keys.Cast<string>()
.ToDictionary(name=>name, name=> _properties[name] as dynamic);
}
}
If the underlying type of the IDictionary does not implement IDictionary<string, dynamic>, you cannot cast the object, period. If it does, a simple cast via (IDictionary<string, dynamic>)localVar will suffice.
If it's not, there are two things you can do:
Copy the IDictionary to your generic type.
Build an Adapter class that accepts the IDictionary as a dependency and implements the generic IDictionary you want, mapping calls from one to the other.
Edit: The sample code you've just posted will copy the dictionary every time it gets called! I will edit again in a moment with some suggested code.
Option 1
your sample code approach is solid as a means of copying the data, but the copy should be cached or you're going to copy lots of times. I'd suggest you put the actual translation code into a separate method and call that from your property the first time it's used. For example:
private IDictionary dataLayerProperties;
private IDictionary<string, dynamic> translatedProperties = null;
protected virtual IDictionary<string, dynamic> Properties
{
if(translatedProperties == null)
{
translatedProperties = TranslateDictionary(dataLayerProperties);
}
return translatedProperties;
}
public IDictionary<string, dynamic> TranslateDictionary(IDictionary values)
{
return values.Keys.Cast<string>().ToDictionary(key=>key, key => values[key] as dynamic);
}
Now, there are obvious cons to this approach... what if dataLayerProperties needs to be refreshed? You have to go setting translatedProperties to null again, etc.
Option 2
This is my preferred approach.
public class TranslatedDictionary : IDictionary<string, dynamic>
{
private IDictionary Original = null;
public TranslatedDictionary(IDictionary original)
{
Original = original;
}
public ICollection<string> Keys
{
get
{
return Original.Keys.Cast<string>().ToList();
}
}
public dynamic this[string key]
{
get
{
return Original[key] as dynamic;
}
set
{
Original[key] = value;
}
}
// and so forth, for each method of IDictionary<string, dynamic>
}
//elsewhere, using your original property and field names:
Properties = new TranslatedDictionary(properties);
Now, there are obvious cons to this approach as well, the most glaring is the fact that the Keys (and Value and anything else that returns ICollection on IDictionary has to return a new array for every call. But this still allows the most flexible approach, since it ensures the data is always up to date.
Unless the backing type for the IDictionary instance already implements IDictionary<string,dynamic> (like Dictionary<string,dynamic>) then casting won't help you. The Cast<>() method is only useful for returning IEnumerable<T> values and normal casting isn't an option.
If providing the data in the form of IDictionary<string,dynamic> is important, then why not go ahead and store it as a Dictionary<string,dynamic> from the start?
Here is a complete implementation of the approach recommended in the top answer. This was too big to fit as a comment.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
/// <summary>
/// The casted dictionary.
/// </summary>
/// <typeparam name="TKey">
/// The key type
/// </typeparam>
/// <typeparam name="TValue">
/// The value type
/// </typeparam>
public class CastedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
/// <summary>
/// The original dictionary.
/// </summary>
private readonly IDictionary originalDictionary;
/// <summary>
/// The keys.
/// </summary>
private ICollection<TKey> keys;
/// <summary>
/// The values.
/// </summary>
private ICollection<TValue> values;
/// <summary>
/// Initializes a new instance of the <see cref="CastedDictionary{TKey,TValue}"/> class.
/// </summary>
/// <param name="original">
/// The original.
/// </param>
public CastedDictionary(IDictionary original)
: this()
{
if (original == null)
{
throw new ArgumentNullException("original");
}
this.originalDictionary = original;
}
/// <summary>
/// Prevents a default instance of the <see cref="CastedDictionary{TKey, TValue}"/> class from being created.
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1642:ConstructorSummaryDocumentationMustBeginWithStandardText", Justification = "Style Cop does not analyze private generic class constructor comments properly")]
private CastedDictionary()
{
}
/// <summary>
/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <returns>
/// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
public int Count
{
get
{
return this.originalDictionary.Count;
}
}
/// <summary>
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
/// </summary>
/// <returns>
/// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
/// </returns>
public bool IsReadOnly
{
get
{
return this.originalDictionary.IsReadOnly;
}
}
/// <summary>
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </returns>
public ICollection<TKey> Keys
{
get
{
return this.keys ?? (this.keys = this.originalDictionary.Keys.Cast<TKey>().ToList());
}
}
/// <summary>
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </returns>
public ICollection<TValue> Values
{
get
{
return this.values ?? (this.values = this.originalDictionary.Values.Cast<TValue>().ToList());
}
}
/// <summary>
/// Gets or sets the element with the specified key.
/// </summary>
/// <returns>
/// The element with the specified key.
/// </returns>
/// <param name="key">The key of the element to get or set.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception><exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and <paramref name="key"/> is not found.</exception><exception cref="T:System.NotSupportedException">The property is set and the <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
public TValue this[TKey key]
{
get
{
return (TValue)this.originalDictionary[key];
}
set
{
this.originalDictionary[key] = value;
}
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>1</filterpriority>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return this.originalDictionary.Cast<KeyValuePair<TKey, TValue>>().GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>2</filterpriority>
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
/// <summary>
/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
public void Add(KeyValuePair<TKey, TValue> item)
{
this.originalDictionary.Add(item.Key, item.Value);
}
/// <summary>
/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
public void Clear()
{
this.originalDictionary.Clear();
}
/// <summary>
/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
/// </summary>
/// <returns>
/// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
/// </returns>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return this.originalDictionary.Contains(item.Key) && EqualityComparer<TValue>.Default.Equals(this[item.Key], item.Value);
}
/// <summary>
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
/// </summary>
/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException">The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.</exception>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
this.originalDictionary.CopyTo(array, arrayIndex);
}
/// <summary>
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <returns>
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
public bool Remove(KeyValuePair<TKey, TValue> item)
{
if (this.Contains(item))
{
this.originalDictionary.Remove(item.Key);
return true;
}
return false;
}
/// <summary>
/// Determines whether the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the specified key.
/// </summary>
/// <returns>
/// true if the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the key; otherwise, false.
/// </returns>
/// <param name="key">The key to locate in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
public bool ContainsKey(TKey key)
{
return this.originalDictionary.Contains(key);
}
/// <summary>
/// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </summary>
/// <param name="key">The object to use as the key of the element to add.</param><param name="value">The object to use as the value of the element to add.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception><exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.</exception><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
public void Add(TKey key, TValue value)
{
this.originalDictionary.Add(key, value);
}
/// <summary>
/// Removes the element with the specified key from the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </summary>
/// <returns>
/// true if the element is successfully removed; otherwise, false. This method also returns false if <paramref name="key"/> was not found in the original <see cref="T:System.Collections.Generic.IDictionary`2"/>.
/// </returns>
/// <param name="key">The key of the element to remove.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
public bool Remove(TKey key)
{
if (this.ContainsKey(key))
{
this.originalDictionary.Remove(key);
return true;
}
return false;
}
/// <summary>
/// Gets the value associated with the specified key.
/// </summary>
/// <returns>
/// true if the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the specified key; otherwise, false.
/// </returns>
/// <param name="key">The key whose value to get.</param><param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
public bool TryGetValue(TKey key, out TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (typeof(TKey).IsValueType == false && key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
{
throw new ArgumentNullException("key");
}
if (this.ContainsKey(key))
{
value = this[key];
return true;
}
value = default(TValue);
return false;
}
}
You need to use a KeyValuePair.
myDictionary.Cast<KeyValuePair<Type1, Type2>>()

Categories