0

I have a interface which currently extends IDictionary<> (and an implementation which Extends Dictionary<>), but I want to have an implementation of this interface which does not allow entries to be added or removed (I want to allow existing entries to be changed though). I could just take the ReadOnlyCollection approach and throw NotSupportedException, but this feels a bit wrong.

Instead I'd like to break the interfaces up so I had one for the accessor bits and one for the mutator bits. This is all ok, except that to do this I ended up with something like this (most methods removed for brevity):

public interface IAccessor<TKey, TValue>
    {
    TValue this [TKey key] { get; set; }
    }

and then my original interface became:

public interface IAttributeDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IAccessor<TKey, TValue>
    {
    new TValue this [TKey key] { get; set; }
    }

And an implementation class defined as:

public class AttributeDictionary<TKey,TValue>: Dictionary<TKey, TValue>, IAttributeDictionary<TKey, TValue> 

I had to make the indexer new to avoid ambiguities between the indexers in IDictionary and IAccessor. the real issue though is that the behaviour of the setter indexer on a Dictionary is to create a new entry in the dictionary. As I want the IAccessor interface to only allow entries to be modified and not created what should I do in the inmplementation of AttributeDictionary? should I have an explicit implementation of the IAccessor indexer method which first checks the given key is in the dictionary and throws an exception if not, or would having 2 indexers with different behaviour be a bad idea? Or should I ditch the indexer in the IAccessor interface and just have a GetValue and SetValue methods instead and avoid the confusion?

Sam Holder
  • 32,535
  • 13
  • 101
  • 181

1 Answers1

1

What strikes me as the problem is that you're still trying to implement IDictionary -- I think you shouldn't implement that interface on your AttributeDictionary (because you don't really support the full functionality mandated by the interface). If, however, you have to support it because you need to send instances of AttributeDictionary to methods that take IDictionary and there are no interfaces higher up in the implementation chain of IDictionary you can use, I think the next best thing is to simply implement IDictionary alone and throw in the indexer's setter.

I feel like the approach you're trying now is just going to lead to subtle bugs down the line where you call the wrong indexer without really knowing it, particularly when working with instances of the class via the interfaces themselves.

EDIT: After Sam's first comment to this answer:

What about an approach like this:

public interface IAccessor<K,V> {
    V this[K key] { get; }
}

public interface IAttributeDictionary<K,V> : IAccessor<K,V>, IDictionary<K,V> {
    // This interface just composes the other two.
}

public class Test<K,V> : IAttributeDictionary<K,V> {
    // This will implement the indexer for both IAccessor and IDictionary.
    // But when the object is accessed as an IAccessor the setter is not available.
    public V this[K key] {
        get { Console.WriteLine("getter"); return default(V); }
        set { Console.WriteLine("setter"); }
    }

    // ...the rest of IDictionary goes here...
}

class Program {
    static void Main (string[] args) {
        // Note that test can be accessed as any of the appropriate types,
        // and the same getter is called.
        Test<string,int> test = new Test<string, int>();
        int a = test["a"];
        int b = ((IDictionary<string, int>)test)["b"];
        int c = ((IAccessor<string, int>)test)["c"];
    }
}

EDIT 2.0: After all the discussion in the comments below, I think I might finally understand the issue, so...

I would argue that IAccessor really shouldn't use an indexer, since (in my opinion) the behavior you want out of it is quite unusual and unexpected. I would instead have GetValueForKey and ChangeValueForKey on IAccessor that can provide the behavior you want, and implement the indexer from IDictionary in the concrete implementation class. If that's unacceptable for any reason, I'd suggest then using explicit interface implementation to implement IAccessor and its indexer in the implementation class -- in both cases, I don't think the new declaration in IAttributeDictionary is necessary.

  • Thanks Josh. Maybe it wasn't clear from my question, but I want AttributeDictionary's setter to allow new values to be added. I only want to dissallow adding new values via the setter in my IAccessor interface (it will be documented as such). Other classes will implement the IAccessor interface alone, which is why I want to make this split in the first place. We will have methods which never need to add new attributes which will only require implementations of the IAccessor interface, but will be able to be passed either objects which can be mutated (like the AttributeDictionary)...ctd – Sam Holder Jan 13 '11 at 17:05
  • or objects which can't (ie those that only implement IAccessor itself) – Sam Holder Jan 13 '11 at 17:06
  • Oh, hmm. I think I misread your question then. Why does IAccessor even have a set method, then? Why not just make it get-only? I think that could simplify the problem. –  Jan 13 '11 at 17:21
  • I edited my answer to show how I think that simplifies things. –  Jan 13 '11 at 17:29
  • Thanks Josh. As I said in my question I need to be able to set the values from IAccessor, just not add or remove entries. Your solution is basically the same as I have at the moment :). – Sam Holder Jan 13 '11 at 22:59
  • I'm confused because you just said, "I only want to dissallow adding new values via the setter in my IAccessor interface," though. The only other way I see to interpret that sentence is that you want "x[k] = v" to succeed if (and only if) x[k] == v already? The distinction I tried to make in my example was that you didn't need the setter or the `new` keyword to hide implementations. –  Jan 14 '11 at 00:16
  • I want to disallow seting of new values *if the given key does not exist already* but it is ok to **change the value** of an existing key. so if `v=x[k]` does not throw an exception then `x[k]=v` is valid but `x[k]=v` should throw an exception if `v=x[k]` would throw an exception. – Sam Holder Jan 14 '11 at 09:24
  • Huh, interesting. That feels like an odd use of indexers, to me, but I've updated my answer again based on that ("EDIT 2.0"). –  Jan 14 '11 at 16:25