Доступ к индексатору неизвестного типа объекта - PullRequest
0 голосов
/ 24 сентября 2019

Есть ли способ, если дан неизвестный объект, чтобы проверить, есть ли у него индексатор, и имеет ли он доступ к значению из него.

Фон я пытаюсь написать собственный конвертер для WPF, которыйпозволяет вытащить предмет из предмета по индексу по линиям.

public class IndexedMultiConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int index = (int)values[1]; // What index

        if (values[0] has indexer)
        {
            return values[0][index];
        }

        return null;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Ответы [ 2 ]

1 голос
/ 24 сентября 2019

Вы можете сделать это с отражением.

Ниже приведен пример доступа к классу с двумя индексаторами с разными типами ключей, если вы всегда уверены, какой тип индексатора у вас есть, это будет немногоменее сложныйНо я думаю, что стоит отметить, что возможен класс с несколькими индексаторами или индексатор с несколькими ключами.

public class IndexedClass
{
    public string SomeProperty { get; set; }

    public int[] SomeArray { get; set; } = new int[] { 3, 4, 5 };

    Hashtable _items = new Hashtable();
    public object this[object key]
    {
        get
        {
            Console.WriteLine("object key");
            return _items[key];
        }
        set
        {
            _items[key] = value;

        }
    }

    public object this[int key]
    {
        get
        {
            Console.WriteLine("int key");
            return _items[key];
        }
        set
        {
            _items[key] = value;
        }
    }
}

обычный доступ к индексатору:

IndexedClass ic = new IndexedClass();
ic["some string"] = "some string value";
Console.WriteLine(ic["some string"]);
ic[1] = 10;
Console.WriteLine(ic[1]);
Console.WriteLine(ic[2]==null);

выбор и доступ к правильномуиндексатор через отражение:

object index = 1;
object myIndexedObject = ic;

Type myIndexType = index.GetType();
var myIndexerProperty = myIndexedObject.GetType().GetProperties().FirstOrDefault(a =>
{
    var p = a.GetIndexParameters();    

    // this will choose the indexer with 1 key 
    // <<public object this[int key]>>, 
    // - of the EXACT type:
    return p.Length == 1 
        && p.FirstOrDefault(b => b.ParameterType == myIndexType) != null;

    // notice that if you call the code below instead,
    // then the <<public object this[object key]>> indexer 
    // will be chosen instead, as it is first in the class,
    // and an <<int>> is an <<object>>

    //return p.Length == 1 
    //    && p.FirstOrDefault(b => b.ParameterType.IsAssignableFrom(myIndexType)) != null;
});

if (myIndexerProperty != null)
{
    object myValue = myIndexerProperty
        .GetValue(myIndexedObject, new object[] { index });

    Console.WriteLine(myValue);
}

Если у вас всегда есть только один индексатор с одним ключом, вы можете сделать это вместо этого, чтобы получить свой индексатор, так как имя свойства индексатора по умолчанию равно "Item":

var myIndexerProperty = myIndexedObject.GetType().GetProperty("Item");

Обратите внимание, что теоретически могут существовать классы со свойством Item, которое не является индексатором, поэтому вам следует проверить, если myIndexerProperty.GetIndexParameters().Length == 1 в любом случае.

1 голос
/ 24 сентября 2019

Единственный 2 способа узнать, имеет ли тип значения индексатор, это:

1) Проверить, если value is IList list, а затем просто сделать return list[index], если это так.

2) Найдите индексатор с помощью отражения, так как тип не должен реализовывать интерфейс IList, чтобы иметь его.

Давайте возьмем этот класс в качестве примера:

class IndexerClass
{
    public object this[int index]
    {
        get
        {
            return (index + 1);
        }
    }

    internal string this[bool index]
    {
        get
        {
            return index.ToString();
        }  
    }

    private int this[IList<int> list, bool defValueIfNone]
    {
        get
        {
            if ((list == null) || (list.Count == 0))
            {
                if (defValueIfNone)
                {
                    return 0;
                }
                throw new ArgumentException("Invalid list");
            }
            return list[0];
        }
    }     
}

Имя, которое используется для индексаторов: Item, обратите внимание, что если у класса есть индексатор (ы), он не может иметь свойства с именем Item, так как он конфликтует с ними.

Чтобы найтииндексатор, который принимает int index, единственный надежный способ сделать это так:

var instance = new IndexerClass();

var type = typeof(IndexerClass); //sins you get a value just do: value.GetType();

var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

if (props.Length > 0)
{
    foreach (var prop in props)
    {
        if (prop.Name == "Item")
        {
            var i_param = prop.GetIndexParameters();

            if (i_param.Length == 1)
            {
                if (i_param[0].ParameterType == typeof(int)) //you can also add `||` and check if the ParameterType is equal to typeof sbyte, byte, short, ushort, uint, long, ulong, float or double.
                {
                    return prop.GetValue(instance, new object[] { 0 });
                }
            }
        }
    }
}
return null;
...