Reputation: 19064
Is it possible to define how to cast a built in object to an interface in C#? Interfaces can't define operators. I have a very simple interface that allow index access, but not mutation:
public interface ILookup<K, V>
{
V this[K key] { get; }
}
I'd like to be able to cast a Dictionary<K, V>
to an ILookup<K, V>
. In my ideal dream world this would look like:
//INVALID C#
public interface ILookup<K, V>
{
static implicit operator ILookup<K, V>(Dictionary<K, V> dict)
{
//Mystery voodoo like code. Basically asserting "this is how dict
//implements ILookup
}
V this[K key] { get; }
}
What I've worked out as a workaround is this:
public class LookupWrapper<K, V> : ILookup<K, V>
{
private LookupWrapper() { }
public static implicit operator LookupWrapper<K, V>(Dictionary<K, V> dict)
{
return new LookupWrapper<K, V> { dict = dict };
}
private IDictionary<K, V> dict;
public V this[K key] { get { return dict[key]; } }
}
This works and means I can now directly cast from Dictionary to ILookup, but boy does it feel convoluted...
Is there a better way to force a conversion to an interface?
Upvotes: 2
Views: 4895
Reputation: 5459
It's a pretty egregious bastardization of the language, but there is a way. I assume your basically trying to apply an interface to a class that already has the signatures you want, you just want a subset (like in your example).
You can use a TransparentProxy/RealProxy pair to create a class at runtime that implements any interface (or MarshalByRef object). There's an abstract method you need to implement to handle the call, but with a little elbow grease you can make this generic enough to be able to handle this kind of situation.
We ran into a similar problem with a library that was very badly wrapped (we had to use reflection to make every method call). So rather than write out a ton of custom reflection code, we wrote the generic case and then wrote a bunch of interfaces that matched the signatures. Then we just used the interfaces directly. We even wrapped/unwrapped objects automatically so we could just make our interface methods return other interfaces and it would all just work.
There's a super-long writeup of the methodology and an implementation at http://blog.paulhounshell.com/?s=Duck
Upvotes: 0
Reputation: 29244
The best you can do is use Extension methods and a wrapper class...
public interface ILookup<K, V>
{
V this[K key] { get; }
}
public class DictWrapper<K, V> : ILookup<K, V>
{
Dictionary<K, V> dictionary;
public DictWrapper(Dictionary<K, V> dictionary)
{
this.dictionary = dictionary;
}
public V this[K key]
{
get { return dictionary[key]; }
}
protected internal Dictionary<K, V> InnerDictionary { get { return dictionary; } }
}
public static class Extensions
{
public static ILookup<K, V> ToLookup<K, V>(this Dictionary<K, V> dictionary)
{
return new DictWrapper<K, V>(dictionary);
}
}
class Program
{
static void Main(string[] args)
{
Dictionary<string, int> data = new Dictionary<string, int>();
data.Add("Office", 100);
data.Add("Walls", 101);
data.Add("Stair", 30);
ILookup<string, int> look = data.ToLookup();
}
}
Upvotes: 0
Reputation: 19064
Thanks to @MBabcock's comment I realized I was thinking about this wrong. I promoted the interface to a full on class as follows:
public class Lookup<K, V>
{
private readonly Func<K, V> lookup;
protected Lookup(Func<K, V> lookup)
{
this.lookup = lookup;
}
public static implicit operator Lookup<K, V>(Dictionary<K, V> dict)
{
return new Lookup<K, V>(k => dict[k]);
}
public V this[K key]
{
get { return lookup(key); }
}
}
Any further conversions I need like this I can simply add an implicit operator
Upvotes: 0
Reputation: 3985
If you have any control over the Dictionary, you can subclass it and override this[]
in your subclass. Then use your new dictionary instead of the .NET one.
Upvotes: 0
Reputation: 437376
Since interfaces cannot include actual code, it follows that you need some class which will "host" the code for the cast. This can either be a class that implements the interface (obviously), or otherwise it needs to be a wrapper class like the one you have¹. There is no third option.
¹The way in which you "invoke" the cast can differ (e.g. you can hide the construction of a LookupWrapper
behind an extension method), but this does not change things.
Upvotes: 2