Reputation: 11832
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
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
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
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