是否可以通过给一个未知对象检查它是否具有索引器,以及是否确实从中访问值。
背景是我正在尝试为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
}
答案 0 :(得分:1)
找出值的类型是否具有索引器的唯一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;
答案 1 :(得分:1)
您可以通过反射来实现。
下面是一个使用两个具有不同类型键的索引器访问一个类的示例,如果您始终确定自己拥有哪种类型的索引器,它会变得简单一些。但是我认为值得注意的是,可以使用具有多个索引器的类或具有多个键的索引器。
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
。