Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if indexing operator exists

I want to be able to:

  • Check if an object has an indexing operator defined.
  • If it is defined, I want to be able to use it.

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
like image 621
nl-x Avatar asked Jan 22 '13 16:01

nl-x


2 Answers

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)

like image 57
Tim Schmelter Avatar answered Oct 13 '22 08:10

Tim Schmelter


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;
like image 33
Olivier Jacot-Descombes Avatar answered Oct 13 '22 07:10

Olivier Jacot-Descombes