I've got a custom Dictionary implementation that covers a private Dictionary, and provides functionality closer to Apple's NSDictionary/NSMutableDictionary in the following two ways:
That's all working great and it's only a few lines of code:
public class NSDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
private Dictionary<TKey, TValue> core = new Dictionary<TKey, TValue>();
public TValue this[TKey key]
{
get { return this.ObjectForKey(key); }
set { this.SetObjectForKey(value, key); }
}
public TValue ObjectForKey(TKey key)
{
try
{
return core[key];
}
catch (Exception exception)
{
return default(TValue);
}
}
public void SetObjectForKey(TValue value, TKey key)
{
try
{
if ( core.ContainsKey(key) )
{
core.Remove(key);
}
core.Add(key, value);
}
catch (Exception exception)
{
// do nothing
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return core.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)core).GetEnumerator();
}
}
But now I'm stuck trying to figure out how to avoid exceptions during allocation/initialization by instead returning null on failure. C# constructors don't allow for this, so I have to add static factory methods ('factory' in the obj-c sense) that have this basic structure...
public static NSDictionary<TKey, TValue> Create()
{
try
{
return new NSDictionary<TKey, TValue>();
}
catch (Exception exception)
{
return null;
}
}
Pardon the not-exactly-on-point example, since the only way that 'new' allocation/initialization is going to fail is if we're out of memory, in which case a more heavy-handed approach like a thrown exception might not be the worst thing in the world.
So goofy example case aside, how would this be done, since calling code needs to know the key/value types and I haven't defined any way here to specify them?
I use this factory pattern throughout all of our C# projects for all of our custom classes, but this is the first time I've had to do it for a generic container class.
Weirdly, that method compiles, but the calling code does not:
NSDictionary<string, Version> dictionary = NSDictionary.Create();
I get:
Error CS0305 Using the generic type 'NSDictionary<TKey, TValue>' requires 2 type arguments
I think the crux of the issue here is that I can't figure out where the '<TKey, TValue>' type declarations should go in this static method situation.
since calling code needs to know the key/value types and I haven't defined any way here to specify them?
Well you have. Since NSDictionary
itself is a generic type, you need to specify its type arguments whenever you call a static method.
// Create should probably take "params KeyValuePair<TKey, TValue>[]"?
var dictionary = NSDictionary<string, Version>.Create();
That said, I'm not sure why Create
here would need to return null. As far as I know, the original NSDictionary
in Objective-C doesn't have such an init
. initWithObjects:forKeys:
ignores any subsequent duplicate keys, instead of returning null or throwing an exception.
If you want something similar to initWithObjects:forKeys:
(though I don't recommend this, since you are writing C#), you can add an Add
method, allowing your C# NSDictionary
to use collection initialisers:
public void Add(TKey, key, TValue value)
{
if (!core.ContainsKey(key))
{
core.Add(key, value);
}
}
Usage:
var dictionary = new NSDictionary<string, string> {
{ "a", "b" },
{ "a", "c" }, // this will not be added
{ "b", "d" }
};
Also, as an aside, I would recommend not using exception handling to implement ObjectForKey
and SetObjectForKey
. You can just check ContainsKey
to avoid the exception:
public TValue ObjectForKey(TKey key)
{
if (core.ContainsKey(key))
{
return core[key];
}
else
{
return default;
}
}
public void SetObjectForKey(TValue value, TKey key)
{
if (core.ContainsKey(key))
{
core[key] = value;
}
else
{
core.Add(key, value);
}
}