nl-x
nl-x

Reputation: 11832

Check if indexing operator exists

I want to be able to:

I want to implement this in the following code. The code contains an object (MyObject) that offers a way to traverse through a multidimensional array or linked set of hash tables. Also it should prevent giving errors if a node in the requested path doesn't exist. The part that I can't figure out is the commented part in the code:

public class MyObject
{
    private object myObject = null;

    public MyObject()
    {
    }

    public MyObject(object value)
    {
        myObject = value;
    }

    public void setValue(object value)
    {
        myObject = value;
    }

    public object getValue()
    {
        return myObject;
    }

    public object this[string key]
    {
        get
        {
            if (myObject == null) 
            {
                return new MyObject(null);
            }
            else
            {
                // determine what of type/class myObject is and if it has indexing operators defined
                // if defined, access them and return the result
                // else return null.
            }
        }
        set
        {
            if (myObject == null)
            {
                // do nothing (or throw an exception);
            }
            else{
                // determine what of type/class myObject is
                // determine if that type/class has indexing operators defined
                // if defined, access them and set the result there
                // else do nothing (or throw an exception).
            }
        }
    }
}

This is what I wish to accomplish:

        // given these variables:
        string loremIpsumString = "lorem ipsum dolor sit amet";
        int[] digits = new int[10];
        for (int i = 0; i <= 3; i++) digits[i] = i;
        Hashtable outerHashtable = new Hashtable();
        Hashtable innerHashtable = new Hashtable();
        innerHashtable.Add("contents", "this is inside");
        outerHashtable.Add("outside", "this is outside");
        outerHashtable.Add("inside", innerHashtable);

        // I can already print this:
        Response.Write(    loremIpsumString    ); // prints "lorem ipsum dolor sit amet"
        Response.Write(    digits[0]    ); // prints "0"
        Response.Write(    digits[1]    ); // prints "1"
        Response.Write(    digits[2]    ); // prints "2"
        Response.Write(    outerHashtable["outside"]    ); // prints "this is outside"
        Response.Write(    ((Hashtable)outerHashtable["inside"])["contents"]    ); // prints "this is outside"

        // But I want to be to do it this way:
        MyObject myObject;

        myObject = new MyObject(loremIpsumString);
        Response.Write(    myObject.getValue()    ); // prints "lorem ipsum dolor sit amet"
        Response.Write(    myObject["unexistant"].getValue()    ); // prints nothing/null
        myObject = new MyObject(digits);
        Response.Write(    myObject[0].getValue()    ); // prints "0"
        Response.Write(    myObject[1].getValue()    ); // prints "1"
        Response.Write(    myObject[2].getValue()    ); // prints "2"
        myObject = new MyObject(outerHashtable);
        Response.Write(    myObject["outside"].getValue()    ); // prints "this is outside"
        Response.Write(    myObject["inside"]["contents"].getValue()    ); // prints "this is inside"
        Response.Write(    myObject["unexistant"].getValue()    ); // prints nothing/null
        Response.Write(    myObject["unexistant"]["unexistant"]["unexistant"].getValue()    ); // prints nothing/null

Upvotes: 7

Views: 3717

Answers (3)

nl-x
nl-x

Reputation: 11832

For others looking for the answer. Here is what I made of it with @TimSchmelter 's help.

So this is the code that I implemented in the get{} that I used in the code at the top of this screen, where in the top of this screen the get{} just contained comments.

get
{
    if (myObject == null)
        return new MyObject(null);
    object returnValue = null;
    bool foundReturnValue = false;
    object[] indexArgs = { key };
    Type myObjectType = myObject.GetType();
    if (typeof(IList).IsAssignableFrom(myObjectType))
    {
        try
        {
            returnValue = ((IList)myObject)[((int)key)];
            foundReturnValue = true;
        }
        catch (Exception) { }
    }
    if (!foundReturnValue)
    {
        foreach (PropertyInfo property in myObjectType.GetProperties())
        {
            ParameterInfo[] indexParameters = property.GetIndexParameters();
            foreach (ParameterInfo indexParameter in indexParameters)
            {
                if (indexParameter.ParameterType.IsAssignableFrom(key.GetType()))
                {
                    try
                    {
                        returnValue = property.GetValue(myObject, indexArgs);
                        foundReturnValue = true;
                    }
                    catch (Exception) { }
                }
                if (foundReturnValue == true)
                    break;
            }
            if (foundReturnValue == true)
                break;
        }
    }
    return new MyObject(returnValue);
}

Upvotes: 0

Tim Schmelter
Tim Schmelter

Reputation: 460058

You can first check if it's inheriting IList to cover (generic) Lists and Arrays. If not you can use PropertyInfo.GetIndexParameters to check if it has an indexer instead:

get
{
    if (myObject == null)
    {
        return null;
    }
    else
    {
        // not sure which index(es) you want
        int index = 0;
        Type t = myObject.GetType();
        if (typeof(IList).IsAssignableFrom(t))
        {
            IList ilist = (IList)myObject;
            return ilist[index];
        }
        else
        {
            var indexer = t.GetProperties()
                .Where(p => p.GetIndexParameters().Length != 0)
                .FirstOrDefault();
            if (indexer != null)
            {
                object[] indexArgs = { index };
                return indexer.GetValue(myObject, indexArgs);
            }
            else
                return null;
        }
    }
}

DEMO (with a string which has an indexer to access the chars)

Upvotes: 9

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112299

You can test if the object is a dictionary

public object this[string key]
{
    get
    {
        var dict = myObject as IDictionary;
        if (dict == null) {
            return null;
        }
        if (dict.Contains(key)) {
            return dict[key];
        }
        return null;
    }
    set
    {
        var dict = myObject as IDictionary;
        if (dict != null) {
            dict[key] = value;
        }
    }
}

Note: If you have the control over the dictionary type to use, then prefer Dictionary<string,object> over Hashtable. Its handy method TryGetValue allows you to safely access it without first calling Contains and thus saves you from accessing it twice. Of cause you would then cast to Dictionary<string,object> instead of IDictionary.

var dict = myObject as Dictionary<string,object>;
if (dict == null) {
    return null;
}
object result;
dict.TryGetValue(key, out result); // Automatically sets result to null
                                   // if an item with this key was not found.
return result;

Upvotes: 1

Related Questions