views:

557

answers:

4

I have a class like this:

public class SomeClass
{
    private const string sessionKey = "__Privileges";
    public Dictionary<int, Privilege> Privileges
    {
        get
        {
            if (Session[sessionKey] == null)
            {
                Session[sessionKey] = new Dictionary<int, Privilege>();
            }

            return (Dictionary<int, Privilege>)Session[sessionKey];
        }
    }
}

Now, if Ido this...

var someClass = new SomeClass();
var p = someClass.Privileges[13];

... and there is no key 13, I will get an error like this:

The given key was not present in the dictionary.

I would like to have a property that can be accessed in the same way as above, but will return a default object in case of the absence of the key.

I tried creating an indexer property like this...

    public Privilege Privileges[int key]
    {
        get
        {
            try { return _privileges[key]; }
            catch { return new Privilege(); }
        }
    }

... but it looks like that's not a C# 2008 language feature.

How can I access the property in the same way, but get the default object if the key isn't present?

+4  A: 

You'll have to define your own IDictionary-based class with an indexer that has the desired behavior, and return an instance of that, rather than the stock Dictionary class, in your property getter.

Pavel Minaev
+1  A: 

Indexers in C# can only be used with the this keyword.

I suspect you want something like this:

public Privilege this[int key]
{
    get
    {
        try { return _privileges[key]; }
        catch { return default(Privelege); }
    }
}

which you can define either directly in SomeClass so that you can access a privelege item like:

SomeClass foo;
var bar = foo[100];

or define this indexer in a custom class that implements from IDictionary<TKey, TValue> (and contains a Dictionary<TKey, TValue internally for actually storing the data). You could then use it like:

SomeClass foo;
var bar = foo.Priveleges[100];

Which is the syntax you seem to propose, and which may be most appropiate, though it takes a bit more effort.

Noldorin
Using exception handling as control flow is a bad idea. It makes your programs hard to debug (because often you want to debug with first chance exception tracking on, and lots of extraneous exceptions make that harder), it makes them slow, and it works against the by-design fact that exceptions are supposed to be exceptional. Far better to instead first check whether the key exists, rather than trying it and catching the exception when it does not.
Eric Lippert
@Eric: Very fair point - I was aware of it before I posted the reply; however, the question was pertaining moreover to the property/indexer aspect of the question. You should probably post your comment on the original question.
Noldorin
A: 

You should use this syntax to retrieve the value:

public Privilege this[int key]
{
    get
    {
        var value = (Privilege)null;
        if(!_privileges.TryGetValue(key, out value))
            value = new Privilege();
        return value;
    }
}

I have a need for this kind of use of IDictionary a lot, so I made some extension methods:

public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> d, TKey key)
{
    TValue v = default(TValue);
    d.TryGetValue(key, out v);
    return v;
}
public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> d, TKey key, Func<TValue> value)
{
    TValue v = d.Get(key);
    if (v == null)
    {
        v = value();
        d.Add(key, v);
    }
    return v;
}

Now you could write:

public Privilege this[int key]
{
    get
    {
        return _privileges.Get(key, () => new Privilege());
    }
}
eulerfx
That's not valid C#! Indexers can only be used with the `this` keyword.
Noldorin
+2  A: 

C# does not supported named indexers, as you have discovered.

Have you considered using a regular method instead of an indexer property? Not every programming problem requires the use fancy syntax to solve. Yes, you could create your own IDictionary implementation with an aggregated dictionary and change the property access behavior - but is that really necessary for something that just fetches a value or returns a default?

I would add a method like this to your class:

protected Privilege GetPrivilege(int key)
{
    try { return _privileges[key]; }
    catch { return new Privilege(); }
}

or better yet, avoid exception handling as a flow control mechanism:

protected Privilege GetPrivilge( int key )
{
    Privilege priv;
    if( _privileges.TryGetValue( key, out priv ) )
        return priv;
    else
        return new Privilege();
}
LBushkin
Good advice. I took your advice to solve my problem. I have used exception handling for flow control before, but haven't thought of it as a bad thing, until now. Thanks.
Ronnie Overby