Ronnie Overby
Ronnie Overby

Reputation: 46470

C# Indexer Property Question

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?

Upvotes: 2

Views: 2236

Answers (4)

LBushkin
LBushkin

Reputation: 131676

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();
}

Upvotes: 6

eulerfx
eulerfx

Reputation: 37709

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());
    }
}

Upvotes: -1

Noldorin
Noldorin

Reputation: 147260

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.

Upvotes: 2

Pavel Minaev
Pavel Minaev

Reputation: 101565

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.

Upvotes: 5

Related Questions