访问未知对象类型的索引器
Access the indexer of an unknown object type
如果给定一个未知对象,有什么方法可以检查它是否有索引器,以及它是否确实从中访问值。
背景是我正在尝试为 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
}
找出一个值的类型是否有索引器的唯一两种方法是:
1) 检查是否 value is IList list
如果是,则执行 return list[index]
。
2) 通过反射找到一个索引器,因为一个类型不需要实现 IList
接口就可以拥有一个。
我们以这个class为例:
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
,请注意,如果 class 有一个索引器,它不能有一个名为 [=15= 的 属性 ] 因为它会与他们发生冲突。
要找到接受 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;
你可以通过反射来完成。
下面是使用两个具有不同类型键的索引器访问 class 的示例,如果您始终确定您拥有哪种类型的索引器,它会稍微简单一些。但我认为值得注意的是,class 具有多个索引器或具有多个键的索引器是可能的。
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");
请注意,虽然理论上可能有 class 带有 属性 的 Item
不是索引器,因此无论如何您都应该检查 myIndexerProperty.GetIndexParameters().Length == 1
。
如果给定一个未知对象,有什么方法可以检查它是否有索引器,以及它是否确实从中访问值。
背景是我正在尝试为 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
}
找出一个值的类型是否有索引器的唯一两种方法是:
1) 检查是否 value is IList list
如果是,则执行 return list[index]
。
2) 通过反射找到一个索引器,因为一个类型不需要实现 IList
接口就可以拥有一个。
我们以这个class为例:
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
,请注意,如果 class 有一个索引器,它不能有一个名为 [=15= 的 属性 ] 因为它会与他们发生冲突。
要找到接受 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;
你可以通过反射来完成。
下面是使用两个具有不同类型键的索引器访问 class 的示例,如果您始终确定您拥有哪种类型的索引器,它会稍微简单一些。但我认为值得注意的是,class 具有多个索引器或具有多个键的索引器是可能的。
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");
请注意,虽然理论上可能有 class 带有 属性 的 Item
不是索引器,因此无论如何您都应该检查 myIndexerProperty.GetIndexParameters().Length == 1
。