Using object references as keys in a Dictionary in C# [duplicate] - c#

Is it possible to use an object as a key for a Dictonary<object, ...> in such a way that the Dictionary treats objects as equal only if they are identical?
For example, in the code below, I want Line 2 to return 11 instead of 12:
Dictionary<object, int> dict = new Dictionary<object, int>();
object a = new Uri("http://www.google.com");
object b = new Uri("http://www.google.com");
dict[a] = 11;
dict[b] = 12;
Console.WriteLine(a == b); // Line 1. Returns False, because a and b are different objects.
Console.WriteLine(dict[a]); // Line 2. Returns 12
Console.WriteLine(dict[b]); // Line 3. Returns 12
The current Dictionary implementation uses object.Equals() and object.GetHashCode() on the keys; but I am looking for a different kind of dictionary that uses the object's identity as a key (instead of the object's value). Is there such a Dictionary in .NET or do I have to implement it from scratch?

You don't need to build your own dictionary - you need to build your own implementation of IEqualityComparer<T> which uses identity for both hashing and equality. I don't think such a thing exists in the framework, but it's easy enough to build due to RuntimeHelpers.GetHashCode.
public sealed class IdentityEqualityComparer<T> : IEqualityComparer<T>
where T : class
{
public int GetHashCode(T value)
{
return RuntimeHelpers.GetHashCode(value);
}
public bool Equals(T left, T right)
{
return left == right; // Reference identity comparison
}
}
I've restricted T to be a reference type so that you'll end up with objects in the dictionary; if you used this for value types you could get some odd results. (I don't know offhand how that would work; I suspect it wouldn't.)
With that in place, the rest is easy. For example:
Dictionary<string, int> identityDictionary =
new Dictionary<string, int>(new IdentityEqualityComparer<string>());

Of course the other answers are entirely correct, but I wrote my own version to suit my needs:
/// <summary>
/// An equality comparer that compares objects for reference equality.
/// </summary>
/// <typeparam name="T">The type of objects to compare.</typeparam>
public sealed class ReferenceEqualityComparer<T> : IEqualityComparer<T>
where T : class
{
#region Predefined
private static readonly ReferenceEqualityComparer<T> instance
= new ReferenceEqualityComparer<T>();
/// <summary>
/// Gets the default instance of the
/// <see cref="ReferenceEqualityComparer{T}"/> class.
/// </summary>
/// <value>A <see cref="ReferenceEqualityComparer<T>"/> instance.</value>
public static ReferenceEqualityComparer<T> Instance
{
get { return instance; }
}
#endregion
/// <inheritdoc />
public bool Equals(T left, T right)
{
return Object.ReferenceEquals(left, right);
}
/// <inheritdoc />
public int GetHashCode(T value)
{
return RuntimeHelpers.GetHashCode(value);
}
}
Design rationale:
The class is sealed.
If the class is not designed to be extended, I'm going to avoid all that expense by sealing it.— Eric Lippert
I know many people (including myself) who believe that classes should indeed be sealed by default.— Jon Skeet
There is an Instance static read-only property to expose a single instance of this class.
It uses Object.ReferenceEquals() instead of == because ReferenceEquals is more explicit.
It uses RuntimeHelpers.GetHashCode() because I don't want to use the possibly overridden GetHashCode of the object, which may not match the behavior of ReferenceEquals. This also avoids a null-check.
It has documentation.

Use your own equality comparer
public class ObjectIdentityEqualityComparer : IEqualityComparer<object>
{
public int GetHashCode(object o)
{
return o.GetHashCode();
}
public bool Equals(object o1, object o2)
{
return object.ReferenceEquals(o1, o2);
}
}
Note that GetHashCode can be overridden, but the crucial check is made with Equals.

As of 5.0, ReferenceEqualityComparer now ships with the runtime.

Use Dictionary with IEqualityComparer<TKey> comparer

Related

Is there a way to find a collection in a class based on its type?

I am thinking of EF's DBContext and DBSets as the background to this question. You can access a particular set using the following code in a Repository class for example.
public TEntity Get(int id)
{
return Context.Set<TEntity>().Find(id);
}
Where Set<TEntity>() returns the set of type TEntity. How exactly is this coded? I tried to find the source code for it to no avail. Would I need to create my own classes and write the logic out in full?
TLDR: EF creates just a DbSet<T> entry in a dictionary where the the key is typeof(T).
Looking at the sourcecode it is implemented as following:
/// <summary>
/// Creates a <see cref="DbSet{TEntity}" /> that can be used to query and save instances of <typeparamref name="TEntity" />.
/// </summary>
/// <typeparam name="TEntity"> The type of entity for which a set should be returned. </typeparam>
/// <returns> A set for the given entity type. </returns>
public virtual DbSet<TEntity> Set<TEntity>()
where TEntity : class
=> (DbSet<TEntity>)((IDbSetCache)this).GetOrAddSet(DbContextDependencies.SetSource, typeof(TEntity));
And Line 195:
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
object IDbSetCache.GetOrAddSet(IDbSetSource source, Type type)
{
CheckDisposed();
if (!_sets.TryGetValue(type, out var set))
{
set = source.Create(this, type);
_sets[type] = set;
}
return set;
}
Where sets is:
private readonly IDictionary<Type, object> _sets = new Dictionary<Type, object>();
I'm guessing Enumerable.OfType<TResult>() will come in handy here. It returns an IEnumerable<TResult> over all the elements of type TResult in the source enumeration. As for its implementation, MSDN has this to say:
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.
I don't know how EF does it, but you could easily accomplish something similar with a Dictionary keyed by Types
private Dictionary<Type, ICollection> registry = new Dictionary<Type, ICollection>();
// adds a collection of a certain type
public void Add<T>(T collection) where T: ICollection {
registry.Add(typeof(T), collection);
}
// create an empty collection of type T and add it to registry
public void InitCollection<T>() where T: ICollection {
registry.Add(typeof(T), (ICollection)Activator.CreateInstance(typeof(T)));
}
// returns a collection of type T if it has been registered
public T Set<T>() where T: ICollection {
return (T)registry[typeof(T)];
}

Value equality for struct keys and reference equality for class keys in a dictionary

I'm implementing a generic dictionary. I want the TKey to be either a struct or a class. If it's a structure I want to compare keys by value, otherwise by reference.
I can't use neither Object.Equals (only works with structs) nor Object.ReferenceEquals (only works with reference types). What method do I use to test for equality then?
== operator would probably solve this issue but I can't use it without specifying any constraints for the key (where TKey : ...). What interfaces should I declare?
I can't use neither Object.Equals (only works with structs) nor Object.ReferenceEquals (only works with reference types).
It seems you may simply be mistaken about how these work. The System.Object.Equals() method implementation works equally well (no pun intended) whether dealing with a value type or a reference type.
For value types, it does a field-by-field comparison. If the two values being compared are the same type, and each of their fields have the same value, then they are considered equal.
For reference types, it simply uses reference equality, as you seem to want.
Note that types can override this method, so the actual implementation used could be different from the above. For example, the string type overrides the method, so that two strings which are not the same instance can still compare as equal. But by default, the above is what happens.
Finally I'll note that if what you want is a behavior that works exactly like the Dictionary<TKey, TValue> class, it may well be that the best solution is to just use that class. :)
You could do the following in your class:
public class MyCustomDictionary<TKey, TValue>
{
private static readonly Func<TKey, TKey, bool> _equalityComparer;
// ... other stuff
static MyCustomDictionary()
{
if (typeof(TKey).IsClass)
_equalityComparer = (lhs, rhs) => Object.ReferenceEquals(lhs, rhs)
else
_equalityComparer = (lhs, rhs) => lhs.Equals(rhs);
}
// ... other stuff
}
And use this equality comparer for comparisons.
The way this is normally done though, is by using an equality comparer IEqualityComparer<TKey>, like this:
public class MyCustomDictionary<TKey, TValue>
{
private readonly IEqualityComparer<TKey> _equalityComparer;
// ... other stuff
public MyCustomDictionary()
{
_equalityComparer = EqualityComparer<T>.Default;
}
public MyCustomDictionary(IEqualityComparer<T> comparer)
{
_equalityComparer = comparer;
}
// ... other stuff
}
This is what is done e.g. in the regular BCL System.Collections.Generic.Dictionary<TKey, TValue>, and other collections that need to do equality comparisons.
If you do not have very special needs (like I initially thought when I read your question), you should use the standard way of doing it with an IEqualityComparer<TKey>.
.NET generic dictionary using Equals and GetHashCode methods, that are virtual and available for both a struct and a class. So you can simply do the same, and just override those methods in you struct:
public struct KeyStructure
{
public override bool Equals(object obj)
{
// your implementation
}
public override int GetHashCode()
{
// your implementation
}
}
On other side, in general, if you want to use interface for type restrictions, you just can create one with the same Equals and GetHashCode methods and add it to any types, that you want to support.
public interface IKey
{
bool Equals(object value);
int GetHashCode();
}
public struct KeyStruct : IKey
{
}
public class KeyClass :IKey
{
}
public class MyDictionary<TKey, TValue> where TKey : IKey
{
}

How to create an empty IReadOnlyCollection

I'm creating an extension method for MultiValueDictionary to encapsulate frequent ContainsKey checks and I was wondering what was the best way to create an empty IReadOnlyCollection?.
What I've used so far is new List<TValue>(0).AsReadOnly() but there must be a better way, an equivilant to IEnumerable's Enumerable.Empty
public static IReadOnlyCollection<TValue> GetValuesOrEmpty<TKey, TValue>(this MultiValueDictionary<TKey, TValue> multiValueDictionary, TKey key)
{
IReadOnlyCollection<TValue> values;
return !multiValueDictionary.TryGetValue(key, out values) ? new List<TValue>(0).AsReadOnly() : values;
}
EDIT: The new .Net 4.6 adds an API to get an empty array: Array.Empty<T> and arrays implement IReadOnlyCollection<T>. This also reduces allocations as it only creates an instance once:
IReadOnlyCollection<int> emptyReadOnlyCollection = Array.Empty<int>();
What I ended up doing is mimicking the implementation of Enumerable.Empty using new TElement[0]:
public static class ReadOnlyCollection
{
public static IReadOnlyCollection<TResult> Empty<TResult>()
{
return EmptyReadOnlyCollection<TResult>.Instance;
}
private static class EmptyReadOnlyCollection<TElement>
{
static volatile TElement[] _instance;
public static IReadOnlyCollection<TElement> Instance
{
get { return _instance ?? (_instance = new TElement[0]); }
}
}
}
Usage:
IReadOnlyCollection<int> emptyReadOnlyCollection = ReadOnlyCollection.Empty<int>();
return new List<XElement>().AsReadOnly();
I don't think there's anything like Enumerable.Empty for read-only collections, but:
List<T> already implements IReadOnlyCollection<T> so you can avoid one object allocation by not calling AsReadOnly() and simply casting the list instead. This is less "safe" in theory but hardly matters in practice.
Alternatively, you could cache the returned ReadOnlyCollection to avoid any object allocation whatsoever (except for the cached object).
As far as I know there is no built in way(Interested to know if one). That said, you can use the following:
IReadOnlyCollection<TValue> readonlyCollection = new ReadOnlyCollection<TValue>(new TValue[] { });
Optionally you can cache the results as it is a ReadOnlyCollection over empty array, It will always be the same no matter how many instances you have.
How's about this which has a similar syntax to Enumerable.Empty:
/// <summary>
/// Contains a method used to provide an empty, read-only collection.
/// </summary>
public static class ReadOnlyCollection
{
/// <summary>
/// Returns an empty, read-only collection that has the specified type argument.
/// </summary>
/// <typeparam name="T">
/// The type to assign to the type parameter of the returned generic read-only collection.
/// </typeparam>
/// <returns>
/// An empty, read-only collection whose type argument is T.
/// </returns>
public static IReadOnlyCollection<T> Empty<T>()
{
return CachedValueProvider<T>.Value;
}
/// <summary/>
static class CachedValueProvider<T>
{
/// <summary/>
public static readonly IReadOnlyCollection<T> Value = new T[0];
}
}
Used like this:
IReadOnlyCollection<int> empty = ReadOnlyCollection.Empty<int>();

Dictionary.ContainsKey() - How does it work?

I've read the MSDN documentation on how Dictionary.ContainsKey() works, but I was wondering how it actually makes the equality comparison? Basically, I have a dictionary keyed to a reference type* and I want the ContainsKey() method to check a certain property of that reference type as its basis for determining if the key exists or not. For example, if I had a Dictionary(MyObject, int) and MyObject has a public property (of int) called "TypeID", could I get ContainsKey(MyObject myObject) to check to see if one of the keys has a TypeID that is equal to myObject? Could I just overload the == operator?
The reference type is an object called "Duration" which holds a value (double Length); "Duration" is a base type used in my music program to denote how long a particular sound lasts. I derive classes from it which incorporate more sophisticated timing concepts, like Western musical time signatures, but want all of them to be comparable in terms of their length.
EDIT: As suggested, I implemented IEquitable on my object like so:
public class Duration : IEquatable<Duration>
{
protected double _length;
/// <summary>
/// Gets or Sets the duration in Miliseconds.
/// </summary>
public virtual double Length
{
get
{
return _length;
}
set
{
_length = value;
}
}
// removed all the other code that as it was irrelevant
public override bool Equals(object obj)
{
Duration otherDuration = (Duration)obj;
if (otherDuration._length == _length)
{
return true;
}
else
{
return false
}
}
}
Is this all I need to do?
EDIT: here is code for your updated example. Note: I find it a little odd that you expose the field as protected, and also have a virtual property that exposes the member. Under this scheme something could override Length resulting in equality that looks at _lenght to not behave as expected.
public class Duration : IEquatable<Duration>
{
protected double _length;
/// <summary>
/// Gets or Sets the duration in Miliseconds.
/// </summary>
public virtual double Length
{
get { return _length; }
set { _length = value; }
}
// removed all the other code that as it was irrelevant
public bool Equals(Duration other)
{
// First two lines are just optimizations
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return _length.Equals(other._length);
}
public override bool Equals(object obj)
{
// Again just optimization
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
// Actually check the type, should not throw exception from Equals override
if (obj.GetType() != this.GetType()) return false;
// Call the implementation from IEquatable
return Equals((Duration) obj);
}
public override int GetHashCode()
{
// Constant because equals tests mutable member.
// This will give poor hash performance, but will prevent bugs.
return 0;
}
}
See EqualityComparer.Default for information on the default IEqualityComparer used by the Dictionary class.
If you do not want to generally override GetHashCode and Equals on the class, or if you are unable to. There is an overload of the Dictionary constructor in which you can provide the specific IEqualityComparer to use.
It is a simple interface to implement, but you do need to be careful that you respect the contract for GetHashCode or you can end up with unexpected behavior.
public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.TypeID == y.TypeID;
}
public int GetHashCode(MyObject obj)
{
return obj.TypeID; //Already an int
}
}
to use it just go
new Dictionary<MyObject, int>(new MyObjectEqualityComparer());
If you want to use the default IEqualityComparer you need to provide roughly the same methods on MyObjectEqualityComparer. You can avoid overriding object.Equals() if you implement IEquatable. However I would strongly discourage it because doing so can create some surprising behavior. You are better of overriding Equals so that you have consistent behavior for all calls to Equals and have hashing that properly matches Equals. I have had to fix a bug in inherited code caused by a past developer only implementing IEquatable.
Internally Dictionary uses EqualityComparer. Firstly it will check whether key implements IEquatable. If key doesn't implement this interface, it will call Equals method.

How do I create a generic converter for units of measurement in C#?

I have been trying to learn a bit more about delegates and lambdas while working on a small cooking project that involves temperature conversion as well as some cooking measurement conversions such as Imperial to Metric and I've been trying to think of a way to make an extensible Unit converter.
Here is what I started with, along with code comments on what some of my plans were. I have no plan to use it like the below, I was just testing out some features of C# I don't know very well, I am also unsure how to take this further. Does anyone have any suggestions on how to create what I am talking about in the comments below? Thanks
namespace TemperatureConverter
{
class Program
{
static void Main(string[] args)
{
// Fahrenheit to Celsius : [°C] = ([°F] − 32) × 5⁄9
var CelsiusResult = Converter.Convert(11M,Converter.FahrenheitToCelsius);
// Celsius to Fahrenheit : [°F] = [°C] × 9⁄5 + 32
var FahrenheitResult = Converter.Convert(11M, Converter.CelsiusToFahrenheit);
Console.WriteLine("Fahrenheit to Celsius : " + CelsiusResult);
Console.WriteLine("Celsius to Fahrenheit : " + FahrenheitResult);
Console.ReadLine();
// If I wanted to add another unit of temperature i.e. Kelvin
// then I would need calculations for Kelvin to Celsius, Celsius to Kelvin, Kelvin to Fahrenheit, Fahrenheit to Kelvin
// Celsius to Kelvin : [K] = [°C] + 273.15
// Kelvin to Celsius : [°C] = [K] − 273.15
// Fahrenheit to Kelvin : [K] = ([°F] + 459.67) × 5⁄9
// Kelvin to Fahrenheit : [°F] = [K] × 9⁄5 − 459.67
// The plan is to have the converters with a single purpose to convert to
//one particular unit type e.g. Celsius and create separate unit converters
//that contain a list of calculations that take one specified unit type and then convert to their particular unit type, in this example its Celsius.
}
}
// at the moment this is a static class but I am looking to turn this into an interface or abstract class
// so that whatever implements this interface would be supplied with a list of generic deligate conversions
// that it can invoke and you can extend by adding more when required.
public static class Converter
{
public static Func<decimal, decimal> CelsiusToFahrenheit = x => (x * (9M / 5M)) + 32M;
public static Func<decimal, decimal> FahrenheitToCelsius = x => (x - 32M) * (5M / 9M);
public static decimal Convert(decimal valueToConvert, Func<decimal, decimal> conversion) {
return conversion.Invoke(valueToConvert);
}
}
}
Update: Trying to clarify my question:
Using just my temperature example below, how would I create a class that contains a list of lambda conversions to Celsius which you then pass it a given temperature and it will try and convert that to Celsius (if the calculation is available)
Example pseudo code:
enum Temperature
{
Celcius,
Fahrenheit,
Kelvin
}
UnitConverter CelsiusConverter = new UnitConverter(Temperature.Celsius);
CelsiusConverter.AddCalc("FahrenheitToCelsius", lambda here);
CelsiusConverter.Convert(Temperature.Fahrenheit, 11);
I thought this was an interesting little problem, so I decided to see how nicely this could be wrapped up into a generic implementation. This isn't well-tested (and doesn't handle all error cases - such as if you don't register the conversion for a particular unit type, then pass that in), but it might be useful. The focus was on making the inherited class (TemperatureConverter) as tidy as possible.
/// <summary>
/// Generic conversion class for converting between values of different units.
/// </summary>
/// <typeparam name="TUnitType">The type representing the unit type (eg. enum)</typeparam>
/// <typeparam name="TValueType">The type of value for this unit (float, decimal, int, etc.)</typeparam>
abstract class UnitConverter<TUnitType, TValueType>
{
/// <summary>
/// The base unit, which all calculations will be expressed in terms of.
/// </summary>
protected static TUnitType BaseUnit;
/// <summary>
/// Dictionary of functions to convert from the base unit type into a specific type.
/// </summary>
static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsTo = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();
/// <summary>
/// Dictionary of functions to convert from the specified type into the base unit type.
/// </summary>
static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsFrom = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();
/// <summary>
/// Converts a value from one unit type to another.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="from">The unit type the provided value is in.</param>
/// <param name="to">The unit type to convert the value to.</param>
/// <returns>The converted value.</returns>
public TValueType Convert(TValueType value, TUnitType from, TUnitType to)
{
// If both From/To are the same, don't do any work.
if (from.Equals(to))
return value;
// Convert into the base unit, if required.
var valueInBaseUnit = from.Equals(BaseUnit)
? value
: ConversionsFrom[from](value);
// Convert from the base unit into the requested unit, if required
var valueInRequiredUnit = to.Equals(BaseUnit)
? valueInBaseUnit
: ConversionsTo[to](valueInBaseUnit);
return valueInRequiredUnit;
}
/// <summary>
/// Registers functions for converting to/from a unit.
/// </summary>
/// <param name="convertToUnit">The type of unit to convert to/from, from the base unit.</param>
/// <param name="conversionTo">A function to convert from the base unit.</param>
/// <param name="conversionFrom">A function to convert to the base unit.</param>
protected static void RegisterConversion(TUnitType convertToUnit, Func<TValueType, TValueType> conversionTo, Func<TValueType, TValueType> conversionFrom)
{
if (!ConversionsTo.TryAdd(convertToUnit, conversionTo))
throw new ArgumentException("Already exists", "convertToUnit");
if (!ConversionsFrom.TryAdd(convertToUnit, conversionFrom))
throw new ArgumentException("Already exists", "convertToUnit");
}
}
The generic type args are for an enum that represents the units, and the type for the value. To use it, you just have to inherit from this class (providing the types) and register some lambdas to do the conversion. Here's an example for temperature (with some dummy calculations):
enum Temperature
{
Celcius,
Fahrenheit,
Kelvin
}
class TemperatureConverter : UnitConverter<Temperature, float>
{
static TemperatureConverter()
{
BaseUnit = Temperature.Celcius;
RegisterConversion(Temperature.Fahrenheit, v => v * 2f, v => v * 0.5f);
RegisterConversion(Temperature.Kelvin, v => v * 10f, v => v * 0.05f);
}
}
And then using it is pretty simple:
var converter = new TemperatureConverter();
Console.WriteLine(converter.Convert(1, Temperature.Celcius, Temperature.Fahrenheit));
Console.WriteLine(converter.Convert(1, Temperature.Fahrenheit, Temperature.Celcius));
Console.WriteLine(converter.Convert(1, Temperature.Celcius, Temperature.Kelvin));
Console.WriteLine(converter.Convert(1, Temperature.Kelvin, Temperature.Celcius));
Console.WriteLine(converter.Convert(1, Temperature.Kelvin, Temperature.Fahrenheit));
Console.WriteLine(converter.Convert(1, Temperature.Fahrenheit, Temperature.Kelvin));
You have a good start, but like Jon said, it's currently not type-safe; the converter has no error-checking to ensure the decimal it gets is a Celsius value.
So, to take this further, I would start introducing struct types that take the numerical value and apply it to a unit of measure. In the Patterns of Enterprise Architecture (aka the Gang of Four design patterns), this is called the "Money" pattern after the most common usage, to denote an amount of a type of currency. The pattern holds for any numeric amount that requires a unit of measure to be meaningful.
Example:
public enum TemperatureScale
{
Celsius,
Fahrenheit,
Kelvin
}
public struct Temperature
{
decimal Degrees {get; private set;}
TemperatureScale Scale {get; private set;}
public Temperature(decimal degrees, TemperatureScale scale)
{
Degrees = degrees;
Scale = scale;
}
public Temperature(Temperature toCopy)
{
Degrees = toCopy.Degrees;
Scale = toCopy.Scale;
}
}
Now, you have a simple type that you can use to enforce that the conversions you are making take a Temperature that is of the proper scale, and return a result Temperature known to be in the other scale.
Your Funcs will need an extra line to check that the input matches the output; you can continue to use lambdas, or you can take it one step further with a simple Strategy pattern:
public interface ITemperatureConverter
{
public Temperature Convert(Temperature input);
}
public class FahrenheitToCelsius:ITemperatureConverter
{
public Temperature Convert(Temperature input)
{
if (input.Scale != TemperatureScale.Fahrenheit)
throw new ArgumentException("Input scale is not Fahrenheit");
return new Temperature(input.Degrees * 5m / 9m - 32, TemperatureScale.Celsius);
}
}
//Implement other conversion methods as ITemperatureConverters
public class TemperatureConverter
{
public Dictionary<Tuple<TemperatureScale, TemperatureScale>, ITemperatureConverter> converters =
new Dictionary<Tuple<TemperatureScale, TemperatureScale>, ITemperatureConverter>
{
{Tuple.Create<TemperatureScale.Fahrenheit, TemperatureScale.Celcius>,
new FahrenheitToCelsius()},
{Tuple.Create<TemperatureScale.Celsius, TemperatureScale.Fahrenheit>,
new CelsiusToFahrenheit()},
...
}
public Temperature Convert(Temperature input, TemperatureScale toScale)
{
if(!converters.ContainsKey(Tuple.Create(input.Scale, toScale))
throw new InvalidOperationException("No converter available for this conversion");
return converters[Tuple.Create(input.Scale, toScale)].Convert(input);
}
}
Because these types of conversions are two-way, you may consider setting up the interface to handle both ways, with a "ConvertBack" method or similar that will take a Temperature in the Celsius scale and convert to Fahrenheit. That reduces your class count. Then, instead of class instances, your dictionary values could be pointers to methods on instances of the converters. This increases the complexity somewhat of setting up the main TemperatureConverter strategy-picker, but reduces the number of conversion strategy classes you must define.
Also notice that the error-checking is done at runtime when you are actually trying to make the conversion, requiring this code to be tested thoroughly in all usages to ensure it's always correct. To avoid this, you can derive the base Temperature class to produce CelsiusTemperature and FahrenheitTemperature structs, which would simply define their Scale as a constant value. Then, the ITemperatureConverter could be made generic to two types, both Temperatures, giving you compile-time checking that you are specifying the conversion you think you are. the TemperatureConverter can also be made to dynamically find ITemperatureConverters, determine the types they will convert between, and automagically set up the dictionary of converters so you never have to worry about adding new ones. This comes at the cost of increased Temperature-based class count; you'll need four domain classes (a base and three derived classes) instead of one. It will also slow the creation of a TemperatureConverter class as the code to reflectively build the converter dictionary will use quite a bit of reflection.
You could also change the enums for the units of measure to become "marker classes"; empty classes that have no meaning other than that they are of that class and derive from other classes. You could then define a full hierarchy of "UnitOfMeasure" classes that represent the various units of measure, and can be used as generic type arguments and constraints; ITemperatureConverter could be generic to two types, both of which are constrained to be TemperatureScale classes, and a CelsiusFahrenheitConverter implementation would close the generic interface to the types CelsiusDegrees and FahrenheitDegrees both derived from TemperatureScale. That allows you to expose the units of measure themselves as constraints of a conversion, in turn allowing conversions between types of units of measure (certain units of certain materials have known conversions; 1 British Imperial Pint of water weighs 1.25 pounds).
All of these are design decisions that will simplify one type of change to this design, but at some cost (either making something else harder to do or decreasing algorithm performance). It's up to you to decide what's really "easy" for you, in the context of the overall application and coding environment you work in.
EDIT: The usage you want, from your edit, is extremely easy for temperature. However, if you want a generic UnitConverter that can work with any UnitofMeasure, then you no longer want Enums to represent your units of measure, because Enums can't have a custom inheritance hierarchy (they derive directly from System.Enum).
You can specify that the default constructor can accept any Enum, but then you have to ensure that the Enum is one of the types that is a unit of measure, otherwise you could pass in a DialogResult value and the converter would freak out at runtime.
Instead, if you want one UnitConverter that can convert to any UnitOfMeasure given lambdas for other units of measure, I would specify the units of measure as "marker classes"; small stateless "tokens" that only have meaning in that they are their own type and derive from their parents:
//The only functionality any UnitOfMeasure needs is to be semantically equatable
//with any other reference to the same type.
public abstract class UnitOfMeasure:IEquatable<UnitOfMeasure>
{
public override bool Equals(UnitOfMeasure other)
{
return this.ReferenceEquals(other)
|| this.GetType().Name == other.GetType().Name;
}
public override bool Equals(Object other)
{
return other is UnitOfMeasure && this.Equals(other as UnitOfMeasure);
}
public override operator ==(Object other) {return this.Equals(other);}
public override operator !=(Object other) {return this.Equals(other) == false;}
}
public abstract class Temperature:UnitOfMeasure {
public static CelsiusTemperature Celsius {get{return new CelsiusTemperature();}}
public static FahrenheitTemperature Fahrenheit {get{return new CelsiusTemperature();}}
public static KelvinTemperature Kelvin {get{return new CelsiusTemperature();}}
}
public class CelsiusTemperature:Temperature{}
public class FahrenheitTemperature :Temperature{}
public class KelvinTemperature :Temperature{}
...
public class UnitConverter
{
public UnitOfMeasure BaseUnit {get; private set;}
public UnitConverter(UnitOfMeasure baseUnit) {BaseUnit = baseUnit;}
private readonly Dictionary<UnitOfMeasure, Func<decimal, decimal>> converters
= new Dictionary<UnitOfMeasure, Func<decimal, decimal>>();
public void AddConverter(UnitOfMeasure measure, Func<decimal, decimal> conversion)
{ converters.Add(measure, conversion); }
public void Convert(UnitOfMeasure measure, decimal input)
{ return converters[measure](input); }
}
You can put in error-checking (check that the input unit has a conversion specified, check that a conversion being added is for a UOM with the same parent as the base type, etc etc) as you see fit. You can also derive UnitConverter to create TemperatureConverter, allowing you to add static, compile-time type checks and avoiding the run-time checks that UnitConverter would have to use.
It sounds like you want something like:
Func<decimal, decimal> celsiusToKelvin = x => x + 273.15m;
Func<decimal, decimal> kelvinToCelsius = x => x - 273.15m;
Func<decimal, decimal> fahrenheitToKelvin = x => ((x + 459.67m) * 5m) / 9m;
Func<decimal, decimal> kelvinToFahrenheit = x => ((x * 9m) / 5m) - 459.67m;
However, you might want to consider not just using decimal, but having a type which knows the units so you can't accidentally (say) apply the "Celsius to Kelvin" conversion to a non-Celsius value. Possibly have a look at the F# Units of Measure approach for inspiration.
You could take a look at Units.NET. It's on GitHub and NuGet. It provides most common units and conversions, supports both static typing and enumeration of units and parsing/printing of abbreviations. It doesn't parse expressions though, and you can't extend existing classes of units, but you can extend it with new third party units.
Example conversions:
Length meter = Length.FromMeters(1);
double cm = meter.Centimeters; // 100
double feet = meter.Feet; // 3.28084
Normally I wanted to add this as a comment to Danny Tuppeny's post, but it seems that I am not able to add this as comment.
I improved the solution from #Danny Tuppeny a little bit. I did not want to add each conversion with two conversation factors, because only one should be necessary. Also the parameter of type Func does not seems to be necessary, it only makes it more complicated for the user.
So my call would looks like:
public enum TimeUnit
{
Milliseconds,
Second,
Minute,
Hour,
Day,
Week
}
public class TimeConverter : UnitConverter<TimeUnit, double>
{
static TimeConverter()
{
BaseUnit = TimeUnit.Second;
RegisterConversion(TimeUnit.Milliseconds, 1000);
RegisterConversion(TimeUnit.Minute, 1/60);
RegisterConversion(TimeUnit.Hour, 1/3600);
RegisterConversion(TimeUnit.Day, 1/86400);
RegisterConversion(TimeUnit.Week, 1/604800);
}
}
I also added a method to get the conversion factor between to units.
This is the modified UnitConverter class:
/// <summary>
/// Generic conversion class for converting between values of different units.
/// </summary>
/// <typeparam name="TUnitType">The type representing the unit type (eg. enum)</typeparam>
/// <typeparam name="TValueType">The type of value for this unit (float, decimal, int, etc.)</typeparam>
/// <remarks>http://stackoverflow.com/questions/7851448/how-do-i-create-a-generic-converter-for-units-of-measurement-in-c
/// </remarks>
public abstract class UnitConverter<TUnitType, TValueType> where TValueType : struct, IComparable, IComparable<TValueType>, IEquatable<TValueType>, IConvertible
{
/// <summary>
/// The base unit, which all calculations will be expressed in terms of.
/// </summary>
protected static TUnitType BaseUnit;
/// <summary>
/// Dictionary of functions to convert from the base unit type into a specific type.
/// </summary>
static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsTo = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();
/// <summary>
/// Dictionary of functions to convert from the specified type into the base unit type.
/// </summary>
static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsFrom = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();
/// <summary>
/// Converts a value from one unit type to another.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="from">The unit type the provided value is in.</param>
/// <param name="to">The unit type to convert the value to.</param>
/// <returns>The converted value.</returns>
public TValueType Convert(TValueType value, TUnitType from, TUnitType to)
{
// If both From/To are the same, don't do any work.
if (from.Equals(to))
return value;
// Convert into the base unit, if required.
var valueInBaseUnit = from.Equals(BaseUnit)
? value
: ConversionsFrom[from](value);
// Convert from the base unit into the requested unit, if required
var valueInRequiredUnit = to.Equals(BaseUnit)
? valueInBaseUnit
: ConversionsTo[to](valueInBaseUnit);
return valueInRequiredUnit;
}
public double ConversionFactor(TUnitType from, TUnitType to)
{
return Convert(One(), from, to).ToDouble(CultureInfo.InvariantCulture);
}
/// <summary>
/// Registers functions for converting to/from a unit.
/// </summary>
/// <param name="convertToUnit">The type of unit to convert to/from, from the base unit.</param>
/// <param name="conversionToFactor">a factor converting into the base unit.</param>
protected static void RegisterConversion(TUnitType convertToUnit, TValueType conversionToFactor)
{
if (!ConversionsTo.TryAdd(convertToUnit, v=> Multiply(v, conversionToFactor)))
throw new ArgumentException("Already exists", "convertToUnit");
if (!ConversionsFrom.TryAdd(convertToUnit, v => MultiplicativeInverse(conversionToFactor)))
throw new ArgumentException("Already exists", "convertToUnit");
}
static TValueType Multiply(TValueType a, TValueType b)
{
// declare the parameters
ParameterExpression paramA = Expression.Parameter(typeof(TValueType), "a");
ParameterExpression paramB = Expression.Parameter(typeof(TValueType), "b");
// add the parameters together
BinaryExpression body = Expression.Multiply(paramA, paramB);
// compile it
Func<TValueType, TValueType, TValueType> multiply = Expression.Lambda<Func<TValueType, TValueType, TValueType>>(body, paramA, paramB).Compile();
// call it
return multiply(a, b);
}
static TValueType MultiplicativeInverse(TValueType b)
{
// declare the parameters
ParameterExpression paramA = Expression.Parameter(typeof(TValueType), "a");
ParameterExpression paramB = Expression.Parameter(typeof(TValueType), "b");
// add the parameters together
BinaryExpression body = Expression.Divide(paramA, paramB);
// compile it
Func<TValueType, TValueType, TValueType> divide = Expression.Lambda<Func<TValueType, TValueType, TValueType>>(body, paramA, paramB).Compile();
// call it
return divide(One(), b);
}
//Returns the value "1" as converted Type
static TValueType One()
{
return (TValueType) System.Convert.ChangeType(1, typeof (TValueType));
}
}
One can define a physical units generic type such that, if one has for each unit a type which implements new and includes a translation method between that unit and a "base unit" of that type, one can perform arithmetic on values that are expressed in different units and have them converted as necessary, using the type system such that a variable of type AreaUnit<LengthUnit.Inches> would only accept things dimensioned in square inches, but if one said myAreaInSquareInches= AreaUnit<LengthUnit.Inches>.Product(someLengthInCentimeters, someLengthInFathoms); it would automatically translate those other unit before performing the multiplication. It can actually work out pretty well when using method-call syntax since methods like Product<T1,T2>(T1 p1, T2 p2) method can accept generic type parameters their operands. Unfortunately, there's no way to make operators generic, nor is there way for a type like AreaUnit<T> where T:LengthUnitDescriptor to define a means of conversion to or from some other arbitrary generic type AreaUnit<U>. An AreaUnit<T> could define conversions to and from e.g. AreaUnit<Angstrom>, but there's no way the compiler could be told that code which is given an AreaUnit<Centimeters> and wantsAreaUnit` can convert inches to angstroms and then to centimeters.

Categories