Mapping System.Drawing.Color - c#

If I don't map Color but map an object that has a Color attribute, FluentNHibernate successfully maps it to a varbinary(max). However, that's extremely inefficient given that realistically Color is just composed of 4 bytes and I am keen on improving it without using a new type to Proxy it.
Internally within Color it is made up of four properties,
long value (cast down to int to represent ARGB)
short state, indicates if this is a known & valid colour
string name, the name of the colour if known.
short knownColor a value to indicate which known colour it is
So I have attempted to map this as follows.
public ColorMapping()
{
CompositeId()
.KeyProperty(c => Reveal.Member<Color, long>("value"))
.KeyProperty(c => Reveal.Member<Color, short>("state"))
.KeyProperty(c => Reveal.Member<Color, string>("name"))
.KeyProperty(c => Reveal.Member<Color, short>("knownColor"));
}
However, on usage I get the following exception,
Class Initialization method DataContextTest.ClassInitialise threw
exception. FluentNHibernate.Cfg.FluentConfigurationException:
FluentNHibernate.Cfg.FluentConfigurationException: An invalid or
incomplete configuration was used while creating a SessionFactory.
Check PotentialReasons collection, and InnerException for more detail.
---> FluentNHibernate.Cfg.FluentConfigurationException: An invalid or
incomplete configuration was used while creating a SessionFactory.
Check PotentialReasons collection, and InnerException for more detail.
---> NHibernate.MappingException: Could not compile the mapping
document: (XmlDocument) ---> NHibernate.MappingException: Could not
determine type for:
System.Linq.Expressions.Expression1[[System.Func2[[System.Drawing.Color,
System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a],[System.Int64, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],
mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]], System.Core, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089, for columns:
NHibernate.Mapping.Column(Member).
Am I misusing Reveal? If so, how am I meant to use it?

i would map it as UserType which converts it back and forth
// in mapping
Map(x => x.Color).Column("ColorARGB").CustomType<ColorUserType>();
[Serializable]
class ColorUserType : IUserType
{
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
bool IUserType.Equals(object x, object y)
{
var colorX = x as Color;
var colorY = y as Color;
return colorX == null ? colorY = null : colorX.ToArgb() == colorY.ToArgb();
}
public virtual int GetHashCode(object x)
{
var colorX = (Color)x;
return (colorX != null) ? colorX.ToArgb() : 0;
}
public bool IsMutable { get { return false; } }
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
return Color.FromArgb((int)NHibernateUtil.Int32.Get(rs, names[0]));
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
NHibernateUtil.Int32.Set(cmd, ((Color)value).ToArgb(), index);
}
public object Replace(object original, object target, object owner)
{
return original;
}
public Type ReturnedType { get { return typeof(Color); } }
public SqlType[] SqlTypes { get { return new []{ SqlTypeFactory.Int32 }; } }
}

I went with an ICompositeUserType in the end.
Code as follows,
public class ColorType : ICompositeUserType
{
/// <summary>
/// Get the value of a property
/// </summary>
/// <param name="component">an instance of class mapped by this "type"</param>
/// <param name="property"/>
/// <returns>
/// the property value
/// </returns>
public object GetPropertyValue(object component, int property)
{
var color = (Color) component;
if (property == 0)
{
return color.ToArgb();
}
return (int) color.ToKnownColor();
}
/// <summary>
/// Set the value of a property
/// </summary>
/// <param name="component">an instance of class mapped by this "type"</param>
/// <param name="property"/>
/// <param name="value">the value to set</param>
public void SetPropertyValue(object component, int property, object value)
{
throw new InvalidOperationException("Color is immutable");
}
/// <summary>
/// Compare two instances of the class mapped by this type for persistence
/// "equality", ie. equality of persistent state.
/// </summary>
/// <param name="x"/><param name="y"/>
/// <returns/>
public new bool Equals(object x, object y)
{
return ReferenceEquals(x, y) ||
x != null && y != null &&
object.Equals(x, y);
}
/// <summary>
/// Get a hashcode for the instance, consistent with persistence "equality"
/// </summary>
public int GetHashCode(object x)
{
return x == null
? 0
: x.GetHashCode();
}
/// <summary>
/// Retrieve an instance of the mapped class from a IDataReader. Implementors
/// should handle possibility of null values.
/// </summary>
/// <param name="dr">IDataReader</param>
/// <param name="names">the column names</param>
/// <param name="session"/>
/// <param name="owner">the containing entity</param>
/// <returns/>
public object NullSafeGet(IDataReader dr, string[] names,
ISessionImplementor session, object owner)
{
var argb = (int?) NHibernateUtil.Int32.NullSafeGet(dr, names[0]);
var knownColor = (int?) NHibernateUtil.Int32.NullSafeGet(dr, names[1]);
return knownColor != null
? Color.FromKnownColor((KnownColor) knownColor.Value)
: Color.FromArgb(argb.Value);
}
/// <summary>
/// Write an instance of the mapped class to a prepared statement.
/// Implementors should handle possibility of null values.
/// A multi-column type should be written to parameters starting from index.
/// If a property is not settable, skip it and don't increment the index.
/// </summary>
/// <param name="cmd"/>
/// <param name="value"/>
/// <param name="index"/>
/// <param name="settable"/>
/// <param name="session"/>
public void NullSafeSet(IDbCommand cmd, object value, int index,
bool[] settable, ISessionImplementor session)
{
var color = (Color) value;
if (color.IsKnownColor)
{
((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
((IDataParameter) cmd.Parameters[index + 1]).Value = (int) color.ToKnownColor();
}
else
{
((IDataParameter) cmd.Parameters[index]).Value = color.ToArgb();
((IDataParameter) cmd.Parameters[index + 1]).Value = DBNull.Value;
}
}
/// <summary>
/// Return a deep copy of the persistent state, stopping at entities and at collections.
/// </summary>
/// <param name="value">generally a collection element or entity field</param>
/// <returns/>
public object DeepCopy(object value)
{
return value;
}
/// <summary>
/// Transform the object into its cacheable representation.
/// At the very least this method should perform a deep copy.
/// That may not be enough for some implementations,
/// method should perform a deep copy. That may not be enough for
/// some implementations, however; for example, associations must
/// be cached as identifier values. (optional operation)
/// </summary>
/// <param name="value">the object to be cached</param>
/// <param name="session"/>
/// <returns/>
public object Disassemble(object value, ISessionImplementor session)
{
return value;
}
/// <summary>
/// Reconstruct an object from the cacheable representation.
/// At the very least this method should perform a deep copy. (optional operation)
/// </summary>
/// <param name="cached">the object to be cached</param>
/// <param name="session"/>
/// <param name="owner"/>
/// <returns/>
public object Assemble(object cached, ISessionImplementor session, object owner)
{
return cached;
}
/// <summary>
/// During merge, replace the existing (target) value in the entity we are merging to
/// with a new (original) value from the detached entity we are merging. For immutable
/// objects, or null values, it is safe to simply return the first parameter. For
/// mutable objects, it is safe to return a copy of the first parameter. However, since
/// composite user types often define component values, it might make sense to recursively
/// replace component values in the target object.
/// </summary>
public object Replace(object original, object target, ISessionImplementor session, object owner)
{
return original;
}
/// <summary>
/// Get the "property names" that may be used in a query.
/// </summary>
public string[] PropertyNames { get { return new[] {"Argb", "KnownColor"}; } }
/// <summary>
/// Get the corresponding "property types"
/// </summary>
public IType[] PropertyTypes
{
get
{
return new IType[]
{
NHibernateUtil.Int32, NHibernateUtil.Int32
};
}
}
/// <summary>
/// The class returned by NullSafeGet().
/// </summary>
public Type ReturnedClass { get { return typeof (Color); } }
/// <summary>
/// Are objects of this type mutable?
/// </summary>
public bool IsMutable { get { return false; } }
}

Related

C# Flagged Enums: how to append value

So, I have a flagged enum:
[Flags]
public enum EmailResult
{
ISPUSuccess = 1,
STSSuccess = 2
}
What I want to do is, via conditonals, set an enum var to one, the other, or both.
As in,
If (ISPU){
Result = EmailResult.ISPUSuccess
}
If (STS){
Result += EmailResult.STSSuccess
}
Like adding.. so it would effectively be
Result = EmailResult.ISPUSuccess | EmailResult.STSSuccess
You use the |= operator to set your values in an enum marked with the [Flags] attribute.
[Flags]
public enum EmailResult
{
None = 0,
ISPUSuccess = 1,
STSSuccess = 2
}
EmailResult result = EmailResult.None;
if(.... your condition ....)
result |= EmailResult.ISPUSuccess;
if( .... other condition ...)
result |= EmailResult.STSSuccess;
Console.WriteLine(result); // -> ISPUSuccess, STSSuccess
if((result & EmailResult.STSSuccess) != EmailResult.None)
..... flag is set ...
Notice that I have added another enum with value 0 to use in conditions where I need to check the current state of a particular flag.
You should use | (or |=) when working with flags. Imagine
Result = EmailResult.ISPUSuccess;
if (someCondition)
Result |= EmailResult.STSSuccess;
if (someOtherCondition)
Result |= EmailResult.STSSuccess;
if both someCondition and someOtherCondition are true you'll have a right result:
1 | 2 | 2 == 3 == 11 (binary)
in case of += however
Result = EmailResult.ISPUSuccess;
if (someCondition)
Result += EmailResult.STSSuccess;
if (someOtherCondition)
Result += EmailResult.STSSuccess;
you'll have
1 + 2 + 2 == 5 == 101 (binary)
which is wrong (please, notice that the second bit in the 101 is reset now).
I use the following generic struct for dealing with enums:
/// <summary>
/// This helper allows for easier manipulation of <see cref="Enum" /> objects with the <see cref="FlagsAttribute" />.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "Flags",
Justification = "This struct acts as a wrapper around Enum Flags, and relates to the term in question.")]
public struct Flags<T> : IEquatable<Flags<T>>, IEquatable<T>
{
#region Fields
private T _value;
#endregion
#region Constructors
/// <summary>
/// This constructor assures that the generic type of <typeparamref name="T" /> is first valid flags enum type.
/// </summary>
/// <exception cref="ArgumentException">Thrown if the type of <typeparamref name="T" /> is not a valid flag enum type.</exception>
[SuppressMessage("Microsoft.Usage", "CA2207:InitializeValueTypeStaticFieldsInline",
Justification = "This static constructor is not initializing any field values, instead, it is performing Type validation on the generic type T that cannot be otherwise performed.")]
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
MessageId = "DMFirmy.Library.Core.Utilities.Throw.InvalidArgumentIf(System.Boolean,System.String,System.String)",
Justification = "This library will not be localized.")]
static Flags()
{
var type = typeof(T);
Throw.InvalidArgumentIf(!type.IsEnum, "Flags<T> can only be used with enum types", nameof(T));
Throw.InvalidArgumentIf(type.GetCustomAttributes(typeof(FlagsAttribute), false).Length == 0, "Flags<T> can only be used with enum types with the Flags attribute", nameof(T));
}
private Flags(T value)
{
_value = value;
}
#endregion
#region Properties
private long ValueInLong
{
get { return GetLongValue(_value); }
set { _value = (T)Enum.ToObject(typeof(T), value); }
}
/// <summary>
/// Gets an enumerable collection of each of the individual flags that compose this instance.
/// </summary>
public IEnumerable<T> AllFlags
{
get
{
var values = (T[])Enum.GetValues(typeof(T));
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var f in values)
{
if (this[f]) yield return f;
}
}
}
#endregion
#region Indexers
/// <summary>
/// This indexer is used to add or check values within the stored flags.
/// </summary>
/// <param name="flags">The flags to search for.</param>
/// <returns>True if the flag is contained, flase otherwise.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
[IndexerName("Item")]
public bool this[T flags]
{
get
{
var flagsInLong = GetLongValue(flags);
return (ValueInLong & flagsInLong) == flagsInLong;
}
set
{
var flagsInLong = GetLongValue(flags);
if(value)
{
ValueInLong |= flagsInLong;
}
else
{
ValueInLong &= ~flagsInLong;
}
}
}
#endregion
#region Methods
/// <summary>
/// Returns true if the given <paramref name="flags" /> are completely contained.
/// </summary>
/// <param name="flags">The flags to check that the underlying value contains.</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
public bool Contains(T flags)
{
return this[flags];
}
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
[SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider",
MessageId = "System.Convert.ToInt64(System.Object)",
Justification = "This library will not be localized.")]
private static long GetLongValue(T flags)
{
return Convert.ToInt64(flags);
}
/// <summary>
/// Indicates whether this instance and first specified object are equal.
/// </summary>
/// <param name="obj">Another object to compare to.</param>
/// <returns>
/// <c>true</c> if <paramref name="obj" /> and this instance are the same type and represent the same value;
/// otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
// ReSharper disable once CanBeReplacedWithTryCastAndCheckForNull
if(obj is T)
{
return Equals((T)obj);
}
if(obj is Flags<T>)
{
return Equals((Flags<T>)obj);
}
return false;
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns><c>true</c> if the current object is equal to the other parameter; otherwise, <c>false</c>.</returns>
public bool Equals(Flags<T> other)
{
return Equals(other._value);
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns><c>true</c> if the current object is equal to the other parameter; otherwise, <c>false</c>.</returns>
public bool Equals(T other)
{
return Equals(_value, other);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return _value.GetHashCode();
}
/// <summary>
/// Returns the string representation of the underlying value.
/// </summary>
/// <returns>The string representation of the underlying value</returns>
public override string ToString()
{
return _value.ToString();
}
#endregion
#region Operators
/// <summary>
/// Implicit conversion from <see cref="Flags{T}" /> to the underlying <typeparamref name="T" /> value.
/// </summary>
/// <param name="flags">The <see cref="Flags{T}" /> value.</param>
/// <returns>
/// The <typeparamref name="T" /> value represented by <paramref name="flags" />
/// </returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
public static implicit operator T(Flags<T> flags)
{
return flags._value;
}
/// <summary>
/// Implicit conversion from the underlying <typeparamref name="T" /> to first <see cref="Flags{T}" /> value.
/// </summary>
/// <param name="flags">The <typeparamref name="T" /> value.</param>
/// <returns>The value of <paramref name="flags" /> as first <see cref="Flags{T}" />.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
public static implicit operator Flags<T>(T flags)
{
return new Flags<T>(flags);
}
/// <summary>
/// Compares two <see cref="Flags{T}" /> instances to determine if they have equal values.
/// </summary>
/// <param name="first">The first <see cref="Flags{T}" />.</param>
/// <param name="second">The second <see cref="Flags{T}" />.</param>
/// <returns><c>true</c> if <paramref name="first" /> and <paramref name="second" /> are equal, <c>false</c> otherwise.</returns>
public static bool operator ==(Flags<T> first, Flags<T> second)
{
return first.Equals(second);
}
/// <summary>
/// Compares two <see cref="Flags{T}" /> instances to determine if they do not have equal values.
/// </summary>
/// <param name="first">The first <see cref="Flags{T}" />.</param>
/// <param name="second">The second <see cref="Flags{T}" />.</param>
/// <returns>
/// <c>true</c> if <paramref name="first" /> and <paramref name="second" /> are not equal, <c>false</c> if they
/// are.
/// </returns>
public static bool operator !=(Flags<T> first, Flags<T> second)
{
return !(first == second);
}
#endregion
}
This struct lets you use your flags enum like an indexed enumerable:
[Flags]
enum MyFlags
{
None = 0,
Value1 = 1,
Value2 = 2,
Value3 = 4
}
Flags<MyFlags> flags = MyFlags.Value1 | MyFlags.Value3;
bool hasValue1 = flags[MyFlags.Value1]; // true
bool hasValue2 = flags[MyFlags.Value2]; // false
bool hasValue3 = flags[MyFlags.Value3]; // true
flags[MyFlags.Value2] = true;
flags[MyFlags.Vlaue1] = false;
hasValue1 = flags[MyFlags.Value1]; // false
hasValue2 = flags[MyFlags.Value2]; // true
hasValue3 = flags[MyFlags.Value3]; // true
bool containsFlag = flags.Contains(MyFlags.Value2 | MyFlags.Value3); // true

Achieve different SELECT and INSERT behavior on NHibernate IUserType

Is there a way to separate the Create (INSERT) behavior from the SELECT behavior.
Let's say I have a database with a column that will return a string looking like this
Predecessors = "1,3,4,5"
In my application I want to use this string like an int array by implementing an IUserType
public interface IIntArray
{
int[] Items { get; set; }
}
public class IntArray : IIntArray
{
public int[] Items { get; set; }
public IntArray(string item)
{
if(string.IsNullOrEmpty(item))
return;
Items = System.Array.ConvertAll<string, int>(item.Split(new[] {','}), int.Parse);
// for older .net versions use the code below
// Items = Array.ConvertAll<string, int>(item.ToString().Split(new[] { ',' }), delegate(string str) { return int.Parse(str); });
}
}
public class IntArrayType : IUserType
{
#region Implementation of IUserType
/// <summary>
/// Compare two instances of the class mapped by this type for persistent "equality"
/// ie. equality of persistent state
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public new bool Equals(object x, object y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.GetType() == y.GetType();
}
/// <summary>
/// Get a hashcode for the instance, consistent with persistence "equality"
/// </summary>
public int GetHashCode(object x)
{
return x.GetHashCode();
}
/// <summary>
/// Retrieve an instance of the mapped class from a resultset.
/// Implementors should handle possibility of null values.
/// </summary>
/// <param name="rs">a IDataReader</param>
/// <param name="names">column names</param>
/// <param name="owner">the containing entity</param>
/// <returns></returns>
/// <exception cref="HibernateException">HibernateException</exception>
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var value = NHibernateUtil.String.NullSafeGet(rs, names[0]);
if (value == null || (string.IsNullOrEmpty(value.ToString())))
{
return null;
}
return new IntArray(value.ToString());
}
/// <summary>
/// Write an instance of the mapped class to a prepared statement.
/// Implementors should handle possibility of null values.
/// A multi-column type should be written to parameters starting from index.
/// </summary>
/// <param name="cmd">a IDbCommand</param>
/// <param name="value">the object to write</param>
/// <param name="index">command parameter index</param>
/// <exception cref="HibernateException">HibernateException</exception>
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
}
else
{
var state = (IIntArray)value;
((IDataParameter)cmd.Parameters[index]).Value = state.GetType().Name;
}
}
/// <summary>
/// Return a deep copy of the persistent state, stopping at entities and at collections.
/// </summary>
/// <param name="value">generally a collection element or entity field</param>
/// <returns>a copy</returns>
public object DeepCopy(object value)
{
return value;
}
/// <summary>
/// During merge, replace the existing (<paramref name="target" />) value in the entity
/// we are merging to with a new (<paramref name="original" />) value from the detached
/// entity we are merging. For immutable objects, or null values, it is safe to simply
/// return the first parameter. For mutable objects, it is safe to return a copy of the
/// first parameter. For objects with component values, it might make sense to
/// recursively replace component values.
/// </summary>
/// <param name="original">the value from the detached entity being merged</param>
/// <param name="target">the value in the managed entity</param>
/// <param name="owner">the managed entity</param>
/// <returns>the value to be merged</returns>
public object Replace(object original, object target, object owner)
{
return original;
}
/// <summary>
/// Reconstruct an object from the cacheable representation. At the very least this
/// method should perform a deep copy if the type is mutable. (optional operation)
/// </summary>
/// <param name="cached">the object to be cached</param>
/// <param name="owner">the owner of the cached object</param>
/// <returns>a reconstructed object from the cachable representation</returns>
public object Assemble(object cached, object owner)
{
return cached;
}
/// <summary>
/// Transform the object into its cacheable representation. At the very least this
/// method should perform a deep copy if the type is mutable. That may not be enough
/// for some implementations, however; for example, associations must be cached as
/// identifier values. (optional operation)
/// </summary>
/// <param name="value">the object to be cached</param>
/// <returns>a cacheable representation of the object</returns>
public object Disassemble(object value)
{
return value;
}
/// <summary>
/// The SQL types for the columns mapped by this type.
/// </summary>
public SqlType[] SqlTypes { get { return new[] { NHibernateUtil.String.SqlType }; } }
/// <summary>
/// The type returned by <c>NullSafeGet()</c>
/// </summary>
public Type ReturnedType { get { return typeof(IntArray); } }
/// <summary>
/// Are objects of this type mutable?
/// </summary>
public bool IsMutable { get { return false; } }
#endregion
}
In my NHibernate class I mapped to property like this
public virtual IntArray Predecessors { get; set; }
And the hbm mapping
<property name="Predecessors" type="Example.IntArrayType, Example" />
The IntArray class does it's job when reading data but when trying to put something back this doesn't work. What I would like to do is to somehow force the IntArray property to render the values of IntArray.Items to a comma separated string
string magic = string.Join(",", Predecessors.Items);
Thanks
Something like this should do the trick
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var ints = value as IntArray;
if(ints != null && ints.Items != null)
{
NHibernate.NHibernateUtil.StringClob.NullSafeSet(cmd, string.Join(", ", ints.Items), index);
}
}

How can I use a dynamic settings.Blah instead of AppSettings["blah"]?

I get how to use dynamic in C# 4.0, however, I'm not sure how to take something and make it dynamic-able (my technical term).
For example, instead of ConfigurationManager.AppSettings["blah"], how can I make a wrapper of sorts that will let me just use it like a dynamic: settings.Blah ?
You still need an entry point. However, from there the possibilities are quite flexible. This is an example idea to demonstrate how powerful dynamic dispatch can be:
public abstract class MyBaseClass
{
public dynamic Settings
{
get { return _settings; }
}
private SettingsProxy _settings = new SettingsProxy();
private class SettingsProxy : DynamicObject
{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var setting = ConfigurationManager.AppSettings[binder.Name];
if(setting != null)
{
result = setting.ToString();
return true;
}
result = null;
return false;
}
}
}
New version with generics !!
public static class WebConfiguration<T>
{
public static dynamic Get {
get { return _settings; }
}
static readonly SettingsProxy _settings = new SettingsProxy ();
class SettingsProxy : DynamicObject
{
public override bool TryGetMember (GetMemberBinder binder, out object result)
{
var setting = ConfigurationManager.AppSettings [binder.Name];
if (setting != null) {
result = Convert.ChangeType (setting, typeof(T));
return true;
}
result = null;
return false;
}
}
}
Usage
WebConfiguration<int>.Get.UserNameLength
Here, let me show you more powers of dynamic dispatch:
/// <summary>
/// Dynamic wrapper for <see cref="AppSettingsSection"/>.
/// </summary>
public class DynamicAppSettings : DynamicObject
{
private string _exeConfigPath;
private System.Configuration.Configuration _config;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicAppSettings"/> class with configuration file
/// of the current application domain.
/// </summary>
public DynamicAppSettings()
: this(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DynamicAppSettings"/> class with the specified configuration file.
/// </summary>
/// <param name="exeConfigPath">Path to configuration file.</param>
public DynamicAppSettings(string exeConfigPath)
{
if (string.IsNullOrEmpty(exeConfigPath))
throw new ArgumentNullException("exeConfigPath");
_exeConfigPath = exeConfigPath;
_config = ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap() { ExeConfigFilename = _exeConfigPath },
ConfigurationUserLevel.None);
}
/// <summary>
/// Gets the application settings entry from the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _config.AppSettings.Settings[binder.Name];
return true;
}
/// <summary>
/// Adds or updates application settings entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (value != null && !(value is string))
{
return base.TrySetMember(binder, value);
}
if (_config.AppSettings.Settings[binder.Name] != null)
{
_config.AppSettings.Settings[binder.Name].Value = value.ToString();
}
else
{
_config.AppSettings.Settings.Add(binder.Name, value.ToString());
}
return true;
}
/// <summary>
/// Invokes <see cref="Configuration.Save"/> method to save changes or removes an entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name.Equals("Save", StringComparison.OrdinalIgnoreCase))
{
_config.Save();
result = null;
return true;
}
if (binder.Name.Equals("Remove", StringComparison.OrdinalIgnoreCase) &&
args != null && args.Length == 1 && (args[0] is string))
{
_config.AppSettings.Settings.Remove(args[0].ToString());
result = null;
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
/// <summary>
/// Dynamic wrapper for <see cref="ConnectionStringsSection"/>.
/// </summary>
public class DynamicConnectionStrings : DynamicObject
{
private string _exeConfigPath;
private System.Configuration.Configuration _config;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicConnectionStrings"/> class with the configuration file
/// of the current application domain.
/// </summary>
public DynamicConnectionStrings()
: this(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DynamicConnectionStrings"/> class with the specified
/// configuration file.
/// </summary>
/// <param name="exeConfigPath">Path to the configuration file.</param>
public DynamicConnectionStrings(string exeConfigPath)
{
if (string.IsNullOrEmpty(exeConfigPath))
throw new ArgumentNullException("exeConfigPath");
_exeConfigPath = exeConfigPath;
_config = ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap() { ExeConfigFilename = _exeConfigPath },
ConfigurationUserLevel.None);
}
/// <summary>
/// Gets the connection string value from the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _config.ConnectionStrings.ConnectionStrings[binder.Name].ConnectionString;
return true;
}
/// <summary>
/// Adds or updates a connection string entry in the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (value != null && !(value is string))
{
return base.TrySetMember(binder, value);
}
if (_config.ConnectionStrings.ConnectionStrings[binder.Name] != null)
{
_config.ConnectionStrings.ConnectionStrings[binder.Name].ConnectionString = value.ToString();
}
else
{
_config.ConnectionStrings.ConnectionStrings.Add(
new ConnectionStringSettings(binder.Name, value.ToString()));
}
return true;
}
/// <summary>
/// Invokes <see cref="Configuration.Save"/> to save changes or removes an entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name.Equals("Save", StringComparison.OrdinalIgnoreCase))
{
_config.Save();
result = null;
return true;
}
if (binder.Name.Equals("Remove", StringComparison.OrdinalIgnoreCase) &&
args != null && args.Length == 1 && (args[0] is string))
{
_config.ConnectionStrings.ConnectionStrings.Remove(args[0].ToString());
result = null;
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
For example:
dynamic apps = new DynamicAppSettings();
apps.UploadLocation = "~/Uploads/";
apps.WebServiceUrl = "http://www.site.com/service.svc";
apps.Remove("TempFolderPath");
apps.Save();
dynamic conns = new DynamicConnectionStrings();
conns.MasterDatabase = "Data Source=lpc:(local);Integrated Security=True;Initial Catalog=master";
conns.Save();

C# Anonymous types cannot be assigned to -- it is read only

What is wrong with this code-snippet?
class Program
{
static void Main(string[] args)
{
var obj = new { Name = "A", Price = 3.003 };
obj.Name = "asdasd";
obj.Price = 11.00;
Console.WriteLine("Name = {0}\nPrice = {1}", obj.Name, obj.Price);
Console.ReadLine();
}
}
I am getting the following errors:
Error 5 Property or indexer 'AnonymousType#1.Name' cannot be assigned to -- it is read only .....\CS_30_features.AnonymousTypes\Program.cs 65 13 CS_30_features.AnonymousTypes
Error 6 Property or indexer 'AnonymousType#1.Price' cannot be assigned to -- it is read only .....\CS_30_features.AnonymousTypes\Program.cs 66 13 CS_30_features.AnonymousTypes
How to re-set values into an anonymous type object?
Anonymous types in C# are immutable and hence do not have property setter methods. You'll need to create a new anonmyous type with the values
obj = new { Name = "asdasd", Price = 11.00 };
Anonymous types are created with read-only properties. You can't assign to them after the object construction.
From Anonymous Types (C# Programming Guide) on MSDN:
Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to first explicitly define a type.
Anonymous types provide a convenient way to encapsulate a set of
read-only properties into a single
object without having to first
explicitly define a type. The type
name is generated by the compiler and
is not available at the source code
level. The type of the properties is
inferred by the compiler. The
following example shows an anonymous
type being initialized with two
properties called Amount and Message.
http://msdn.microsoft.com/en-us/library/bb397696.aspx
Use ExpandoObject instead since it supports updating/adding new properties after object creation (it has been around since since C# 4).
Note that it is important to declare the object using the keyword dynamic (instead of var)
using System.Dynamic;
dynamic person = new ExpandoObject();
person.FirstName = "John";
person.LastName = "Doe";
/// <summary>
/// Of 8 bytes.
/// A structure which represents an:
/// <see cref="System.Object"/>, <see cref="System.Array"/> or <see cref="System.String"/>.
/// </summary>
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit, Size = 8)]
public struct Invariant
{
[System.Runtime.InteropServices.FieldOffset(0)]
public System.ValueType Value;
[System.Runtime.InteropServices.FieldOffset(0)]
public System.Array Array;
[System.Runtime.InteropServices.FieldOffset(0)]
public object Object;
[System.Runtime.InteropServices.FieldOffset(0)]
public string String;
/// <summary>
/// Used to interpret the address/memory of object which can be thought of as IntPtr*
/// Remember to deference the type 1 time to inspect the memory where ref object points to.
/// </summary>
/// <typeparam name="TType"></typeparam>
/// <param name="index"></param>
/// <param name="length"></param>
/// <returns></returns>
public System.Span<TType> GetSpan<TType>(int index, int length) => System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref System.Runtime.CompilerServices.Unsafe.Add(ref System.Runtime.CompilerServices.Unsafe.As<object, TType>(ref Object), index), length);
/// <summary>
/// Get a <see cref="System.IntPtr"/> which points to the address of <see cref="Object"/>
/// </summary>
/// <returns></returns>
public System.IntPtr ToPointer() => GetSpan<System.IntPtr>(0, 1)[0] + System.IntPtr.Size + System.IntPtr.Size; //Syncbloc, Rtti
/// <summary>
///
/// </summary>
/// <returns></returns>
public bool IsAligned() => (ulong)ToPointer() % (ulong)System.IntPtr.Size == 0;
/// <summary>
/// Allowing one to set the <see cref="System.Type"/> of <see cref="Object"/>
/// </summary>
public System.Type Type
{
set
{
System.Runtime.InteropServices.Marshal.WriteIntPtr(GetSpan<System.IntPtr>(0, 1)[0], value.TypeHandle.Value);
//System.Runtime.CompilerServices.Unsafe.AsRef(System.Runtime.CompilerServices.Unsafe.As<object, System.IntPtr>(ref Object)) = value.TypeHandle.Value;
//System.Runtime.CompilerServices.Unsafe.AsRef(GetSpan<System.IntPtr>(0, 1)[0]) = value.TypeHandle.Value;
}
}
}
/// <summary>
/// A single value
/// Will implicitly convert to <see cref="Invariant"/>
/// </summary>
/// <typeparam name="T"></typeparam>
public ref struct Invariant<T>
{
public static System.RuntimeTypeHandle TypeHandle => typeof(T).TypeHandle;
public static System.IntPtr ToPointer(ref T t)
{
System.IntPtr rtti = System.Runtime.CompilerServices.Unsafe.As<T, System.IntPtr>(ref t);
//rtti = System.Runtime.InteropServices.Marshal.ReadIntPtr(rtti, 0);
return rtti;
}
/// <summary>
/// The value
/// </summary>
public T Value;
/// <summary>
/// Implicit create <see cref="Invariant"/>
/// </summary>
/// <param name="src"></param>
public static implicit operator Invariant(Invariant<T> src) => new Invariant() { Object = src.Value };
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static T Emplace(ref Invariant invariant, bool writeHeader) => Emplace(invariant.ToPointer(), writeHeader);
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static T Emplace(ref Invariant invariant) => Emplace(invariant.ToPointer(), false == System.Type.GetTypeFromHandle(TypeHandle).IsValueType);//Might need to read the void* in the spann which is is different places depending on the runtime.
/// <summary>
/// Emplace the value initializing the Object header and TypeHandle.
/// </summary>
/// <param name="bufferPtr"></param>
/// <returns></returns>
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static T Emplace(System.IntPtr bufferPtr, bool writeHeader = true)
{
var dataPointer = bufferPtr + 1;
//New way
if (writeHeader) System.Runtime.CompilerServices.Unsafe.AsRef(in bufferPtr) = System.IntPtr.Zero;
System.Runtime.CompilerServices.Unsafe.AsRef(in dataPointer) = typeof(T).TypeHandle.Value;
//Old way
//if (writeHeader) System.Runtime.InteropServices.Marshal.WriteIntPtr(bufferPtr, System.IntPtr.Zero); //Object Header
//System.Runtime.InteropServices.Marshal.WriteIntPtr(dataPointer, typeof(T).TypeHandle.Value);
return System.Runtime.CompilerServices.Unsafe.As<System.IntPtr, T>(ref dataPointer); //Pointer to the method table pointer
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static T Emplace(ref T t, bool writeHeader = true) => Emplace(System.Runtime.CompilerServices.Unsafe.AsRef(in System.Runtime.CompilerServices.Unsafe.As<T, System.IntPtr>(ref t)), writeHeader);
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static T Emplace(ref T t) => Emplace(ref t, false == System.Type.GetTypeFromHandle(TypeHandle).IsValueType);
/// <summary>
/// Useful when ref t has been Emplaced
/// </summary>
/// <param name="expression"></param>
/// <param name="t"></param>
/// <param name="arguments"></param>
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static void Construct(System.Linq.Expressions.Expression<System.Action> expression, ref T t, params object[] arguments)
{
//figure out which constructorInfo
System.Linq.Expressions.NewExpression newExpression = expression.Body as System.Linq.Expressions.NewExpression;
//newExpression.Arguments
//DeclaringType should equal typeof(T)
//typeof(T).GetConstructor(System.Array.Empty<System.Type>()).Invoke(t, arguments);
//don't read the object returned here its on the stack and boxed refer to t
if (null == arguments) arguments = System.Array.Empty<object>();
newExpression.Constructor.Invoke(t, arguments);
//Could get paramters from expression
var instantiator = System.Linq.Expressions.Expression.Lambda/*<System.Func<T>>*/(newExpression).Compile();
instantiator.Method.Invoke(t, arguments);
}
//[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
//public unsafe static T Alloc(System.Span<byte> bytes) => Alloc((System.IntPtr)System.Runtime.CompilerServices.Unsafe.AsPointer(ref bytes.GetPinnableReference()));
//Could be exposed from a static Construct<T>(ref T) method but which constructor to invoke?
//Could also accept an Expression and use the expression to build a delegate
//Alloc(()=>new Whatever(1,2,3,"4"));
//When you have the expression the call to the correct MethodInfo (constructor) is already performed via overload resolution.
//
//private static unsafe class Constructor
//{
// private static readonly delegate*<T, void> ConstructorHandle;
// static Constructor()
// {
// System.Type type = typeof(T);
// System.Reflection.ConstructorInfo constructorInfo = type.GetConstructor(System.Array.Empty<System.Type>());
// Constructor.ConstructorHandle = (delegate*<T, void>)constructorInfo.MethodHandle.GetFunctionPointer();
// }
// [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
// internal static void Invoke(T returnObject)
// {
// Constructor.ConstructorHandle(returnObject);
// }
//}
/// <summary>
/// Creates a <see cref="System.Span{T}"/>
/// </summary>
/// <returns></returns>
public System.Span<T> GetSpan() => System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref Value, 1);
/// <summary>
/// Create a <see cref="System.Span{TType}"/>
/// </summary>
/// <typeparam name="TType"></typeparam>
/// <param name="index"></param>
/// <param name="length"></param>
/// <returns></returns>
public System.Span<TType> GetSpan<TType>(int index, int length) => System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref System.Runtime.CompilerServices.Unsafe.Add(ref System.Runtime.CompilerServices.Unsafe.As<T, TType>(ref Value), index), length);
}
/// <summary>
/// Provides extensions useful for <see cref="System.Span{T}"/>
/// </summary>
public static class SpanExtensions
{
/// <summary>
/// Pointer to the first element
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="span"></param>
/// <returns></returns>
public static System.IntPtr ToPointer<T>(this System.Span<T> span)
{
//ptr to struct to interior ptr
ref T pp = ref span[0];
ref byte b = ref System.Runtime.CompilerServices.Unsafe.As<T, byte>(ref pp);
//Interior pointer layout and size may vary with such
//Object header is 24 bytes and 1 more pointer for the interior ptr itself
b = ref System.Runtime.CompilerServices.Unsafe.Add(ref b,(System.IntPtr)(-System.IntPtr.Size * 4));
//from bite to pointer
ref System.IntPtr ptrSpan = ref System.Runtime.CompilerServices.Unsafe.As<byte, System.IntPtr>(ref b);
return ptrSpan;
}
public static int ReadLength(ref System.IntPtr ptr) => System.Runtime.InteropServices.Marshal.ReadInt32(ptr, System.IntPtr.Size);
public static System.Span<T> RecoverSpan<T>(System.IntPtr ptr)
{
//Because of reloc on the stack 1 must keep 2 pts, 1 to the length and 1 to the last element
var pb = ptr + (System.IntPtr.Size * 4);
ref byte reverse = ref System.Runtime.CompilerServices.Unsafe.As<System.IntPtr, byte>(ref pb);
ref T pp = ref System.Runtime.CompilerServices.Unsafe.As<byte, T>(ref reverse);
System.Span<T> span = System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref pp, System.Runtime.InteropServices.Marshal.ReadInt32(ptr, System.IntPtr.Size));
return span;
}

What is the generic version of a Hashtable?

I have been learning the basics of generics in .NET. However, I don't see the generic equivalent of Hashtable. Please share some sample C# code for creating generic hashtable classes.
Dictionary<TKey, TValue>
Note that Dictionary is not a 100% drop in replacement for HashTable.
There is a slight difference in the way they handle NULLs.
The dictionary will throw an exception if you try to reference a key that doesn't exist. The HashTable will just return null.
The reason is that the value might be a value type, which cannot be null. In a Hashtable the value was always Object, so returning null was at least possible.
The generic version of Hashtable class is System.Collections.Generic.Dictionary class.
Sample code:
Dictionary<int, string> numbers = new Dictionary<int, string>( );
numbers.Add(1, "one");
numbers.Add(2, "two");
// Display all key/value pairs in the Dictionary.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Key: " + kvp.Key + "\tValue: " + kvp.Value);
}
The generic version of a Hashtable is the Dictionary<TKey,TValue> class (link). Here is some sample code translated from using a Hashtable into the most direct equivalent of Dictionary (argument checking removed for sake of brevity)
public HashTable Create(int[] keys, string[] values) {
HashTable table = new HashTable();
for ( int i = 0; i < keys.Length; i++ ) {
table[keys[i]] = values[i];
}
return table;
}
public Dictionary<object,object> Create(int[] keys, string[] values) {
Dictionary<object,object> map = Dictionary<object,object>();
for ( int i = 0; i < keys.Length; i++) {
map[keys[i]] = values[i];
}
return map;
}
That's a fairly direct translation. But the problem is that this does not actually take advantage of the type safe features of generics. The second function could be written as follows and be much more type safe and inccur no boxing overhead
public Dictionary<int,string> Create(int[] keys, string[] values) {
Dictionary<int,string> map = Dictionary<int,string>();
for ( int i = 0; i < keys.Length; i++) {
map[keys[i]] = values[i];
}
return map;
}
Even better. Here's a completely generic version
public Dictionary<TKey,TValue> Create<TKey,TValue>(TKey[] keys, TValue[] values) {
Dictionary<TKey,TValue> map = Dictionary<TKey,TValue>();
for ( int i = 0; i < keys.Length; i++) {
map[keys[i]] = values[i];
}
return map;
}
And one that is even further flexible (thanks Joel for pointing out I missed this)
public Dictionary<TKey,TValue> Create<TKey,TValue>(
IEnumerable<TKey> keys,
IEnumerable<TValue> values) {
Dictionary<TKey,TValue> map = Dictionary<TKey,TValue>();
using ( IEnumerater<TKey> keyEnum = keys.GetEnumerator() )
using ( IEnumerator<TValue> valueEnum = values.GetEnumerator()) {
while (keyEnum.MoveNext() && valueEnum.MoveNext() ) {
map[keyEnum.Current] = valueEnum.Current;
}
}
return map;
}
For those who are interested, I created a generic Hashtable wrapper class, which is useful for enforcing type safety and can be passed as a generic IDictionary, ICollection and IEnumerable type, whereas the non-generic Hashtable cannot. Below is the implementation.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Common.Collections.Generic
{
public class Hashtable<TKey, TValue> : IDictionary<TKey, TValue>
, ICollection<KeyValuePair<TKey, TValue>>
, IEnumerable<KeyValuePair<TKey, TValue>>
, IDictionary
, ICollection
, IEnumerable
{
protected Hashtable _items;
/// <summary>
/// Initializes a new, empty instance of the Hashtable class using the default initial capacity, load factor, hash code provider, and comparer.
/// </summary>
public Hashtable()
{
_items = new Hashtable();
}
/// <summary>
/// Initializes a new, empty instance of the Hashtable class using the specified initial capacity, and the default load factor, hash code provider, and comparer.
/// </summary>
/// <param name="capacity">The approximate number of elements that the Hashtable object can initially contain. </param>
public Hashtable(int capacity)
{
_items = new Hashtable(capacity);
}
/// <summary>
/// Actual underlying hashtable object that contains the elements.
/// </summary>
public Hashtable Items { get { return _items; } }
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
/// </summary>
/// <param name="key">Key of the new element to add.</param>
/// <param name="value">Value of the new elment to add.</param>
public void Add(TKey key, TValue value)
{
_items.Add(key, value);
}
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
/// </summary>
/// <param name="item">Item containing the key and value to add.</param>
public void Add(KeyValuePair<TKey, TValue> item)
{
_items.Add(item.Key, item.Value);
}
void IDictionary.Add(object key, object value)
{
this.Add((TKey)key, (TValue)value);
}
/// <summary>
/// Add a list of key/value pairs to the hashtable.
/// </summary>
/// <param name="collection">List of key/value pairs to add to hashtable.</param>
public void AddRange(IEnumerable<KeyValuePair<TKey, TValue>> collection)
{
foreach (var item in collection)
_items.Add(item.Key, item.Value);
}
/// <summary>
/// Determines whether the Hashtable contains a specific key.
/// </summary>
/// <param name="key">Key to locate.</param>
/// <returns>True if key is found, otherwise false.</returns>
public bool ContainsKey(TKey key)
{
return _items.ContainsKey(key);
}
/// <summary>
/// Determines whether the Hashtable contains a specific key.
/// </summary>
/// <param name="item">Item containing the key to locate.</param>
/// <returns>True if item.Key is found, otherwise false.</returns>
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return _items.ContainsKey(item.Key);
}
bool IDictionary.Contains(object key)
{
return this.ContainsKey((TKey)key);
}
/// <summary>
/// Gets an ICollection containing the keys in the Hashtable.
/// </summary>
public ICollection<TKey> Keys
{
get { return _items.ToList<TKey>(); }
}
ICollection IDictionary.Keys
{
get { return this.Keys.ToList(); }
}
/// <summary>
/// Gets the value associated with the specified key.
/// </summary>
/// <param name="key">The key of the value to get.</param>
/// <param name="value">When this method returns, contains the value associated with the specified key,
/// if the key is found; otherwise, the default value for the type of the value parameter. This parameter
/// is passed uninitialized.</param>
/// <returns>true if the hashtable contains an element with the specified key, otherwise false.</returns>
public bool TryGetValue(TKey key, out TValue value)
{
value = (TValue)_items[key];
return (value != null);
}
/// <summary>
/// Gets an ICollection containing the values in the Hashtable.
/// </summary>
public ICollection<TValue> Values
{
get { return _items.Values.ToList<TValue>(); }
}
ICollection IDictionary.Values
{
get { return this.Values.ToList(); }
}
/// <summary>
/// Gets or sets the value associated with the specified key.
/// </summary>
/// <param name="key">The key whose value to get or set. </param>
/// <returns>The value associated with the specified key. If the specified key is not found,
/// attempting to get it returns null, and attempting to set it creates a new element using the specified key.</returns>
public TValue this[TKey key]
{
get
{
return (TValue)_items[key];
}
set
{
_items[key] = value;
}
}
/// <summary>
/// Removes all elements from the Hashtable.
/// </summary>
public void Clear()
{
_items.Clear();
}
/// <summary>
/// Copies all key/value pairs in the hashtable to the specified array.
/// </summary>
/// <param name="array">Object array to store objects of type "KeyValuePair<TKey, TValue>"</param>
/// <param name="arrayIndex">Starting index to store objects into array.</param>
public void CopyTo(Array array, int arrayIndex)
{
_items.CopyTo(array, arrayIndex);
}
/// <summary>
/// Copies all key/value pairs in the hashtable to the specified array.
/// </summary>
/// <param name="array">Object array to store objects of type "KeyValuePair<TKey, TValue>"</param>
/// <param name="arrayIndex">Starting index to store objects into array.</param>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_items.CopyTo(array, arrayIndex);
}
/// <summary>
/// Gets the number of key/value pairs contained in the Hashtable.
/// </summary>
public int Count
{
get { return _items.Count; }
}
/// <summary>
/// Gets a value indicating whether the Hashtable has a fixed size.
/// </summary>
public bool IsFixedSize
{
get { return _items.IsFixedSize; }
}
/// <summary>
/// Gets a value indicating whether the Hashtable is read-only.
/// </summary>
public bool IsReadOnly
{
get { return _items.IsReadOnly; }
}
/// <summary>
/// Gets a value indicating whether access to the Hashtable is synchronized (thread safe).
/// </summary>
public bool IsSynchronized
{
get { return _items.IsSynchronized; }
}
/// <summary>
/// Gets an object that can be used to synchronize access to the Hashtable.
/// </summary>
public object SyncRoot
{
get { return _items.SyncRoot; }
}
/// <summary>
/// Removes the element with the specified key from the Hashtable.
/// </summary>
/// <param name="key">Key of the element to remove.</param>
public void Remove(TKey key)
{
_items.Remove(key);
}
/// <summary>
/// Removes the element with the specified key from the Hashtable.
/// </summary>
/// <param name="item">Item containing the key of the element to remove.</param>
public void Remove(KeyValuePair<TKey, TValue> item)
{
this.Remove(item.Key);
}
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
var numValues = _items.Count;
_items.Remove(key);
return numValues > _items.Count;
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
var numValues = _items.Count;
_items.Remove(item.Key);
return numValues > _items.Count;
}
void IDictionary.Remove(object key)
{
_items.Remove(key);
}
/// <summary>
/// Returns an enumerator that iterates through the hashtable.
/// </summary>
/// <returns>An enumerator for a list of key/value pairs.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (DictionaryEntry? item in _items)
yield return new KeyValuePair<TKey, TValue>((TKey)item.Value.Key, (TValue)item.Value.Value);
}
/// <summary>
/// Returns an enumerator that iterates through the hashtable.
/// </summary>
/// <returns>An enumerator for a list of key/value pairs as generic objects.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
// Very old enumerator that no one uses anymore, not supported.
throw new NotImplementedException();
}
object IDictionary.this[object key]
{
get
{
return _items[(TKey)key];
}
set
{
_items[(TKey)key] = value;
}
}
}
}
I have done some testing of this Hashtable vs Dictionary and found the two perform about the same when used with a string key and string value pair, except the Hashtable seems to use less memory. The results of my test are as follows:
TestInitialize Dictionary_50K_Hashtable
Number objects 50000, memory usage 905164
Insert, 22 milliseconds.
A search not found, 0 milliseconds.
Search found, 0 milliseconds.
Remove, 0 milliseconds.
Search found or not found, 0 milliseconds.
TestCleanup Dictionary_50K_Hashtable
TestInitialize Dictionary_50K_Dictionary
Number objects 50000, memory usage 1508316
Insert, 16 milliseconds.
A search not found, 0 milliseconds.
Search found, 0 milliseconds.
Remove, 0 milliseconds.
Search found or not found, 0 milliseconds.
TestCleanup Dictionary_50K_Dictionary
The generic version of System.Collection.Hashtable is System.Collections.Generic.Dictionary<TKey, TValue>.

Categories

Resources