检查索引运算符是否存在。

7
我希望您能够:
  • 检查对象是否定义了索引运算符。
  • 如果已定义,则可以使用它。

我想在以下代码中实现此功能。 该代码包含一个对象(MyObject),该对象提供了一种遍历多维数组或链接哈希表集的方法。此外,它应该防止在请求的路径中存在节点时出现错误。 我无法理解代码中注释部分的内容:

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).
            }
        }
    }
}

这是我希望完成的事情:
        // 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

好的,我现在明白了。抱歉之前没有跟上前几次阅读。 - lc.
3个回答

9

你可以首先检查它是否继承了 IList 来适用于(泛型)Lists 和数组。如果没有,你可以使用PropertyInfo.GetIndexParameters 来检查它是否有一个索引器:

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

演示(使用具有索引器访问字符的字符串


@nl-x:你需要使用 using System.Linq; - Olivier Jacot-Descombes
@nl-x: 通常Linq更易读。但请使用您想使用的任何内容。查询并不是我的答案的关键部分。重点在于 GetIndexParameters().Lengthindexer.GetValue - Tim Schmelter
@nl-x: 编辑了我的答案,提供了一种适用于数组和列表的方法,因为两者都继承自IList - Tim Schmelter
@TimSchmelter: 虽然你的解决方案对我的问题确实有帮助,但似乎并不是真正回答问题的方法,即如何知道索引运算符是否存在以及如何使用它们。当然,一定有一种方法可以找出对象/类是否设置了索引操作符(方括号[])。为什么需要使用if-数组-iList-else-GetIndexParameters的解决方案呢?(虽然我将标记您的答案为已接受) - nl-x
一个数组/列表没有使用索引器来访问它的值。 索引器只是使用类似数组的语法,但实际上是属性。 这就是为什么所有集合的 GetIndexParameters.Length 都是 0。 我已经展示了访问数组/列表的值非常容易和高效,不需要使用反射。 这就是为什么我首先检查它是否可以转换为 ILIst,因为 ILIst 是所有集合(即使是泛型)的基本接口。 - Tim Schmelter

1
您可以测试该对象是否为字典。
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;
        }
    }
}

注意:如果您可以控制要使用的字典类型,则应优先选择Dictionary<string,object>而不是Hashtable。它方便的方法TryGetValue允许您在不首先调用Contains的情况下安全地访问它,从而避免了访问两次。当然,您将转换为Dictionary<string,object>而不是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;

我之前尝试过走这条路,但是失败得很惨。我会再试一次! - nl-x

0

对于其他寻找答案的人。这是我在@TimSchmelter的帮助下得出的结论。

所以这就是我在get{}中实现的代码,我在屏幕顶部的代码中使用了它,在屏幕顶部的get{}中只包含注释。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接