2

I have a small class that implements a dictionary that maps from the type of an interface to an implementation of that interface that extends from a base class. Unfortunately the abstract base class does not implement the interfaces, so once in the dictionary, there seems to be no way to associate the two. There is another method in this class that is dependent on storing the objects as BaseClass (in fact, most of my class is dependent on that--the getter into the dictionary is somewhat of a convenience).

private readonly Dictionary<Type, BaseClass> dictionary;

public void Add<T>(BaseClass base)
{
    if (!(base is T))  // How to get rid of this check?
    {
        throw new ArgumentException("base does not implement " + typeof(T).Name);
    }

    this.dictionary.Add(typeof(T), base);
}

public T Get<T>()
{
    BaseClass base;
    this.dictionary.TryGetValue(typeof(T), out base);
    return (T)(object)base;  // How to get rid of (object) cast?
}

Are there any clever constraints I can use to remove the (base is T) check, the cast to object, or both?

Here is the class setup, for reference:

class BaseClass { }
interface IThing { }
class MyClass : BaseClass, IThing { }

dict.Add<IThing>(new MyClass());
IThing myClass = dict.Get<IThing>();
lukegravitt
  • 892
  • 4
  • 12

2 Answers2

1

The only way to get the compile-time enforcement you're looking for would be if you have compile-type knowledge of the derived type being added.

For example, if you also specify a type parameter for the class being added then you could constrain that the class implement the interface type parameter:

    public void Add<TInterface, TClass>(TClass @base)
        where TClass : BaseClass, TInterface {
        this.dictionary.Add(typeof(TInterface), @base);
    }

So you could do this:

    MyClass ok = new MyClass();
    dict.Add<IThing, MyClass>(ok);

But not this:

    class MyClassNotIThing : BaseClass { }

    MyClassNotIThing notOk = new MyClassNotIThing();
    dict.Add<IThing, MyClassNotIThing>(notOk);

Aside from that, generic constraints don't offer a means by which to constrain that a known type (i.e. BaseClass) inherit from a generic type parameter.

cokeman19
  • 2,405
  • 1
  • 25
  • 40
  • I'm about halfway down this path now by using a trick described [here](http://stackoverflow.com/questions/2893698/partial-generic-type-inference-possible-in-c), I got rid of the check in Add() using type inference. Getting rid of the cast in Get() may be more effort than it's worth since I know the cast is safe by virtue of controlled access through the Add(). – lukegravitt Jul 12 '13 at 02:18
0

Here is the solution I ended up using. There are a few tricks that can make the Add() safe without the check (see the link in a comment to cokeman19's answer), but I opted not to do that as I find this code a bit cleaner.

interface IThing { }

abstract class BaseClass
{
    internal T AsInterface<T> where T : class
    {
        return this as T;
    }
}

class MyClass : BaseClass, IThing { }

class DictionaryClass
{
    private readonly Dictionary<Type, BaseClass> dictionary;

    public void Add<T>(BaseClass base)
    {
        if (base is T)
        {
            dictionary.Add(typeof(T), base);
        }
    }

    public T Get<T>() where T : class
    {
        return dictionary[typeof(T)].AsInterface<T>();
    }
}

Note that this solution does allow calls like:

myClass.AsInterface<IThingItDoesntImplement>() 

but this returns null and I made the function internal to prevent strange uses anyway.

lukegravitt
  • 892
  • 4
  • 12